This concept (mixing 2 streams of equal length together such that you end up getting one element from each stream both passed to a single function which then maps the combination to something else) is called zipping.
Java's stream classes doesn't include any support for this operation. What you have here also isn't something you can meaningfully zip together unless you can somehow guarantee that both will have this 'id' concept happening in the exact same order (i.e. if your sizes
is [{3, large}, {2, small}]
instead, we're no longer talking about a zip operation).
In the former case (it's not about 'combine red and small because they both have id 2
, it's about combine the 2
and red
from the first element in colors
with the small
from the first element in sizes
, regardless of ids), you can either find zip support from another library, or hack it:
if (colors.size() != sizes.size()) throw new IllegalStateException();
IntStream.range(0, colors.size())
.mapToObj(i -> new Thing(colors.get(i).getId(), colors.get(i).getColor(), sizes.get(i).getSize()))
.collect(Collectors.toList());
It looks ugly, it has ridiculously bad performance if the lists aren't random access, in in pretty much all ways I can fathom this is such a disappointment compared to just making 2 iterators and parallel-iterating them:
if (colors.size() != sizes.size()) throw new IllegalStateException();
var itColors = colors.iterator();
var itSizes = sizes.iterator();
var out = new ArrayList<Thing>();
while (itColors.hasNext()) {
Thing a = itColors.next();
out.add(new Thing(a.getId(), a.getColor(), itSizes.next().getSize());
}
that you should definitely not use streams in the first place if this is how you want to approach it.
No, it's about the IDs.
In that case, again streams seem very much like you using a very shiny fancy hammer you found to smear your jam on your sandwich: Streams just... make no sense here. At all. You can try some hacky weirdo stuff that, well, makes this happen and uses streams, but it'll be flaky code: It'll look weird, be non-idiomatic, include a ton of caveats (assumptions that, if not true, mean the code fails to do the job right, or has extremely bad performance characteristics in certain cases), and in general just be strictly worse.
The solution is maps:
var out = new HashMap<Long, Thing>();
for (Thing c : colors) out.put(c.getId(), c);
for (Thing s : sizes) out.merge(c.getId(), s,
(a, b) -> new Thing(a.getId(), a.getColor(), b.getSize());
look at how clean that is, and it also deals with any mismatches between your inputs (sizes contains id 87, but colors doesn't).