<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Ceycode Dev Hub Blog</title>
        <link>https://CeyCode.github.io/dev-hub/blog</link>
        <description>Ceycode Dev Hub Blog</description>
        <lastBuildDate>Fri, 22 May 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[TIL: Stream.toList() vs Collectors.toList() in Java]]></title>
            <link>https://CeyCode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist</link>
            <guid>https://CeyCode.github.io/dev-hub/blog/2026/05/22/til-stream-tolist-vs-collectors-tolist</guid>
            <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Java 16 added Stream.toList() — but it's not a drop-in replacement for Collectors.toList().]]></description>
            <content:encoded><![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:encoded>
            <category>TIL</category>
            <category>Java</category>
            <category>Streams</category>
        </item>
        <item>
            <title><![CDATA[Shrink your Docker images with multi-stage builds]]></title>
            <link>https://CeyCode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds</link>
            <guid>https://CeyCode.github.io/dev-hub/blog/2026/05/20/docker-multi-stage-builds</guid>
            <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Multi-stage builds let you compile inside Docker and ship only what the app actually needs — no build tools in production.]]></description>
            <content:encoded><![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:encoded>
            <category>Tutorial</category>
            <category>Docker</category>
            <category>Containers</category>
        </item>
    </channel>
</rss>