<div dir="ltr">Greetings to the core libs folks. I have been using Gatherers extensively for my job in a past few months, and would like to share some of the gatherers that I have been extensively using, so maybe some of them may be a source of inspiration for evolving the Gatherers class.<br><br>1. eagerlyConsume()<br>Implementation:<div><div style="background-color:rgb(30,31,34);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace"><span style="color:rgb(207,142,109)">public static </span><<span style="color:rgb(22,186,172)">T</span>> Gatherer<<span style="color:rgb(22,186,172)">T</span>, ?, <span style="color:rgb(22,186,172)">T</span>> <span style="color:rgb(86,168,245)">eagerlyConsume</span>() {<br>    <span style="color:rgb(207,142,109)">return </span>Gatherer.<span style="font-style:italic">of</span>(<br>        ArrayList<<span style="color:rgb(22,186,172)">T</span>>::<span style="color:rgb(207,142,109)">new</span>,<br>        (list, val, downstream) -> {<br>            list.add(val);<br>            <span style="color:rgb(207,142,109)">return true</span>;<br>        },<br>        (left, right) -> {<br>            left.addAll(right);<br>            <span style="color:rgb(207,142,109)">return </span>left;<br>        },<br>        (list, downstream) -> {<br>            <span style="color:rgb(207,142,109)">for </span>(<span style="color:rgb(207,142,109)">var </span>item : list) {<br>                downstream.push(item);<br>            }<br>        }<br>    );<br>}</pre></div></div><div>Purpose: many times, i need to perform a concurrent mapping of jpa entities to dtos. Unfortunately, mapConcurrent does not accept custom executors, which i need in order to propagate auth, transaction and other contexts. So, therefore, I previously have used following pattern:<br><br>stream().map(COmpletableFuture.supplyAsync(..., executor)).toList().stream().map(CompletableFuture::join)<br><br>toList is required here to eagerly start all futures, as otherwise the will actually launch sequentially due to the pulling nature of streams. With gatherer, on the other hand, i can achieve following:<br>stream().map(COmpletableFuture.supplyAsync(..., executor))..gather<font color="#000000" face="arial, sans-serif">(eagerlyConsume())</font>

.map(CompletableFuture::join), which looks much more readable, and (presumably, haven't actually verified it) should have better performance<br><br>2. ofCollector<br>Implementation:<br><div style="background-color:rgb(30,31,34);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace"><span style="color:rgb(207,142,109)">public </span><<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">R</span>> Gatherer<<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">R</span>> <span style="color:rgb(86,168,245)">ofCollector</span>(Collector<<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">R</span>> collector) {<br>    <span style="color:rgb(207,142,109)">return </span>Gatherer.<span style="font-style:italic">of</span>(<br>        collector.supplier(),<br>        (a, t, _) -> {<br>            <span style="color:rgb(199,125,187)">collector</span>.accumulator().accept(a, t);<br>            <span style="color:rgb(207,142,109)">return true</span>;<br>        },<br>        collector.combiner(),<br>        (state, downstream) -> downstream.push(<span style="color:rgb(199,125,187)">collector</span>.finisher().apply(state))<br>    );<br>}</pre></div>Pretty self explanatory, this is just an adapter of collector to gatherer, allowing arbitrary collector-defined folds<br><br>3. collectThenFlatten & co<br>Implementations:<br></div><div><div style="background-color:rgb(30,31,34);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace"><span style="color:rgb(207,142,109)">public static </span><<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">R </span><span style="color:rgb(207,142,109)">extends </span>Collection<<span style="color:rgb(22,186,172)">T</span>>> Gatherer<<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">T</span>> <span style="color:rgb(86,168,245)">collectThenFlatten</span>(Collector<<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">R</span>> collector) {<br>    <span style="color:rgb(207,142,109)">return </span>Gatherer.<span style="font-style:italic">of</span>(<br>        collector.supplier(),<br>        (a, t, _) -> {<br>            <span style="color:rgb(199,125,187)">collector</span>.accumulator().accept(a, t);<br>            <span style="color:rgb(207,142,109)">return true</span>;<br>        },<br>        collector.combiner(),<br>        (state, downstream) -> {<br>            <span style="color:rgb(207,142,109)">for </span>(<span style="color:rgb(207,142,109)">var </span>item : <span style="color:rgb(199,125,187)">collector</span>.finisher().apply(state)) {<br>                downstream.push(item);<br>            }<br>        }<br>    );<br>}<br><br><span style="color:rgb(207,142,109)">public static </span><<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">K</span>, <span style="color:rgb(22,186,172)">V</span>, <span style="color:rgb(22,186,172)">R </span><span style="color:rgb(207,142,109)">extends </span>Map<<span style="color:rgb(22,186,172)">K</span>, <span style="color:rgb(22,186,172)">V</span>>> Gatherer<<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, Map.Entry<<span style="color:rgb(22,186,172)">K</span>, <span style="color:rgb(22,186,172)">V</span>>> <span style="color:rgb(86,168,245)">collectThenFlattenEntries</span>(Collector<<span style="color:rgb(22,186,172)">T</span>, <span style="color:rgb(22,186,172)">A</span>, <span style="color:rgb(22,186,172)">R</span>> collector) {<br>    <span style="color:rgb(207,142,109)">return </span>Gatherer.<span style="font-style:italic">of</span>(<br>        collector.supplier(),<br>        (a, t, _) -> {<br>            <span style="color:rgb(199,125,187)">collector</span>.accumulator().accept(a, t);<br>            <span style="color:rgb(207,142,109)">return true</span>;<br>        },<br>        collector.combiner(),<br>        (state, downstream) -> {<br>            <span style="color:rgb(207,142,109)">for </span>(<span style="color:rgb(207,142,109)">var </span>entry : <span style="color:rgb(199,125,187)">collector</span>.finisher().apply(state).entrySet()) {<br>                downstream.push(entry);<br>            }<br>        }<br>    );<br>}</pre></div></div><div>These are more specialized adapters of collector adapters, mostly a convenience for avoiding flatMapping results, THough, I would like to note that <font face="arial, sans-serif" style="" color="#000000">collectThenFlattenEntries is mostly used specifically with groupingBy collector, to avoid following nasty chains:<br><br>collect(groupingBy(...)).entrySet().stream()<br><br>Maybe it's just my personal preferences, but i really dislike this back n forth from stream to map, then to set and to stream again, so this gatherer seems pretty pleasant to use<br><br>That's basically all that I wanted to share regarding this topic, hope this experience will have some value for core libs maintainers<br><br>Best regards</font></div></div>