<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://CeyCode.github.io/dev-hub/blog</id>
    <title>Ceycode Dev Hub Blog</title>
    <updated>2026-05-22T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://CeyCode.github.io/dev-hub/blog"/>
    <subtitle>Ceycode Dev Hub Blog</subtitle>
    <icon>https://CeyCode.github.io/dev-hub/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[TIL: Stream.toList() vs Collectors.toList() in Java]]></title>
        <id>https://CeyCode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist</id>
        <link href="https://CeyCode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist"/>
        <updated>2026-05-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Java 16 added Stream.toList() — but it's not a drop-in replacement for Collectors.toList().]]></summary>
        <content type="html"><![CDATA[<p>Java 16 introduced <code>Stream.toList()</code> as a shorter way to collect a stream into a list. Looks like a simple convenience method — turns out there's a meaningful difference.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="context">Context<a href="https://ceycode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist#context" class="hash-link" aria-label="Direct link to Context" title="Direct link to Context" translate="no">​</a></h2>
<p>I was simplifying some stream pipelines and swapped <code>collect(Collectors.toList())</code> for the newer <code>Stream.toList()</code>. Tests started failing with <code>UnsupportedOperationException</code>. Not obvious why at first.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-discovery">The discovery<a href="https://ceycode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist#the-discovery" class="hash-link" aria-label="Direct link to The discovery" title="Direct link to The discovery" translate="no">​</a></h2>
<p><code>Stream.toList()</code> returns an <strong>unmodifiable</strong> list. <code>Collectors.toList()</code> returns a regular mutable <code>ArrayList</code>.</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Collectors.toList() — mutable, you can add/remove after</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">List</span><span class="token generics punctuation" style="color:#393A34">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:#393A34">&gt;</span><span class="token plain"> mutable </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> stream</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">collect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Collectors</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toList</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mutable</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"extra"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// fine</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Stream.toList() — unmodifiable</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">List</span><span class="token generics punctuation" style="color:#393A34">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:#393A34">&gt;</span><span class="token plain"> immutable </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> stream</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toList</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">immutable</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"extra"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// throws UnsupportedOperationException</span><br></div></code></pre></div></div>
<p><code>Collectors.toUnmodifiableList()</code> existed before Java 16 and gives the same behaviour as <code>Stream.toList()</code> — so the new method is really a shorthand for the unmodifiable version, not the mutable one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-it-matters">Why it matters<a href="https://ceycode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist#why-it-matters" class="hash-link" aria-label="Direct link to Why it matters" title="Direct link to Why it matters" translate="no">​</a></h2>
<p>If you're collecting into a list you then mutate (sorting in place, removing elements, etc.), the new shorthand will break. The fix is straightforward — use <code>Collectors.toList()</code> when you need mutability, <code>Stream.toList()</code> when you don't — but the failure mode is a runtime exception, not a compile error.</p>
<p><strong>Prefer <code>Stream.toList()</code> by default</strong> (immutability is usually the right default), and reach for <code>Collectors.toList()</code> only when you need to mutate the result.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source">Source<a href="https://ceycode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist#source" class="hash-link" aria-label="Direct link to Source" title="Direct link to Source" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://bugs.openjdk.org/browse/JDK-8180352" target="_blank" rel="noopener noreferrer" class="">JDK-8180352</a> — the JEP that introduced <code>Stream.toList()</code></li>
<li class=""><a href="https://openjdk.org/projects/jdk/16/" target="_blank" rel="noopener noreferrer" class="">Java 16 release notes</a></li>
</ul>]]></content>
        <author>
            <name>Kavinda Dewmith</name>
            <uri>https://github.com/kavinda</uri>
        </author>
        <category label="TIL" term="TIL"/>
        <category label="Java" term="Java"/>
        <category label="Streams" term="Streams"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Shrink your Docker images with multi-stage builds]]></title>
        <id>https://CeyCode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds</id>
        <link href="https://CeyCode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Multi-stage builds let you compile inside Docker and ship only what the app actually needs — no build tools in production.]]></summary>
        <content type="html"><![CDATA[<p>Build tools, test dependencies, and intermediate artifacts have no business being in your production image. Multi-stage builds solve this cleanly.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-with-single-stage-builds">The problem with single-stage builds<a href="https://ceycode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds#the-problem-with-single-stage-builds" class="hash-link" aria-label="Direct link to The problem with single-stage builds" title="Direct link to The problem with single-stage builds" translate="no">​</a></h2>
<p>A naive Dockerfile that installs Maven, compiles a Spring Boot app, and runs it ends up with Maven, the JDK, and all your source code in the final image — easily 600–800 MB.</p>
<div class="language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># single-stage — too much in the final image</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">FROM eclipse-temurin:21</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">WORKDIR /app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">COPY . .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">RUN ./mvnw package -DskipTests</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CMD ["java", "-jar", "target/app.jar"]</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="multi-stage-build-in-one-image-run-from-another">Multi-stage: build in one image, run from another<a href="https://ceycode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds#multi-stage-build-in-one-image-run-from-another" class="hash-link" aria-label="Direct link to Multi-stage: build in one image, run from another" title="Direct link to Multi-stage: build in one image, run from another" translate="no">​</a></h2>
<div class="language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># Stage 1: compile</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">FROM eclipse-temurin:21 AS build</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">WORKDIR /app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">COPY pom.xml .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">COPY src ./src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">RUN ./mvnw package -DskipTests</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Stage 2: run — just the JRE and the jar</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">FROM eclipse-temurin:21-jre AS runtime</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">WORKDIR /app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">COPY --from=build /app/target/app.jar app.jar</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EXPOSE 8080</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ENTRYPOINT ["java", "-jar", "app.jar"]</span><br></div></code></pre></div></div>
<p>The <code>--from=build</code> instruction copies only what you explicitly ask for from the build stage. Docker discards everything else.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="result">Result<a href="https://ceycode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds#result" class="hash-link" aria-label="Direct link to Result" title="Direct link to Result" translate="no">​</a></h2>
<table><thead><tr><th>Approach</th><th>Image size</th></tr></thead><tbody><tr><td>Single-stage (JDK + Maven + source)</td><td>~750 MB</td></tr><tr><td>Multi-stage (JRE only)</td><td>~230 MB</td></tr></tbody></table>
<p>Smaller image = faster pulls, smaller attack surface, less to scan for vulnerabilities.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tips">Tips<a href="https://ceycode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds#tips" class="hash-link" aria-label="Direct link to Tips" title="Direct link to Tips" translate="no">​</a></h2>
<ul>
<li class="">Name your stages (<code>AS build</code>, <code>AS runtime</code>) so <code>--from=</code> is readable</li>
<li class="">Cache the dependency download layer separately by copying <code>pom.xml</code> before <code>src/</code> — Maven will skip re-downloading if <code>pom.xml</code> hasn't changed</li>
<li class="">Use a distroless or Alpine JRE for even smaller images</li>
</ul>
<hr>
<p><em>Have a Docker tip? <a class="" href="https://ceycode.github.io/dev-hub/kb/contributing">Write a TIL or article</a> and share it with the team.</em></p>]]></content>
        <author>
            <name>Ceycode Engineering</name>
            <uri>https://github.com/ceycode</uri>
        </author>
        <category label="Tutorial" term="Tutorial"/>
        <category label="Docker" term="Docker"/>
        <category label="Containers" term="Containers"/>
    </entry>
</feed>