6

I am trying to get into Java 8 and get my head around streams and lambdas to solve various problems and got stuck on this specific one which I normally use a forEach and store the values in a Map to solve.

How would you write the code to get the expected list using the new features in Java 8 ?

List<Integer> voterA = Arrays.asList(1,2,3,4,5);
List<Integer> voterB = Arrays.asList(1,2,3,4,5);
List<List<Integer>> votes = Arrays.asList(voterA, voterB);

// expected list = (2,4,6,8,10)
List<Integer> sumVotes = ...

2 Answers2

7

That one isn't really doable the way you're hoping. The closest you could get would probably be

IntStream.range(0, voterA.size())
    .mapToObj(i -> voterA.get(i) + voterB.get(i))
    .collect(toList());

...but there's no "zip" operation on streams, largely because two different streams can have backing spliterators that split at different points, so you can't line them up properly.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 3
    It's annoying that I can see exactly how I'd do this in C# in LINQ - in a pretty general way - but I'm still groping around when it comes to Java streams. Maybe when I get more practice... – Jon Skeet Feb 18 '15 at 19:50
  • 1
    I mean...I look at the Spliterator interface, and I don't think there's any _nice_ way to do zipping, just because `trySplit` allows the split to happen at an arbitrary point. You can do indexing efficiently with SUBSIZED spliterators, and fall back to a less-efficient sequential approach for other spliterators, but I don't think there's anything you can do with zipping. – Louis Wasserman Feb 18 '15 at 19:52
  • 1
    Here's the C# version, complete with 3 voters with different lengths of lists: https://gist.github.com/jskeet/2c1db126e424cf58cb69 – Jon Skeet Feb 18 '15 at 19:56
  • 4
    I mean, that translates pretty directly to https://gist.github.com/lowasser/e1b57e1a9358cbf52cc6 – Louis Wasserman Feb 18 '15 at 19:59
  • Right. It's really just the primitives aspect (and the explicit stream call) that makes it uglier than the C# - your version doesn't have the conditional handling of lists of unequal sizes, but even I can work out how I'd do that ;) – Jon Skeet Feb 18 '15 at 22:15
  • 2
    @LouisWasserman Getting the spliterators to line up their splits, especially in parallel, is certainly a big issue. Another issue is that you really want the result of zipping two (or N) streams to be another stream. What would its element type be? A `Pair` or `Tuple` class? Or provide a `BiFunction` that combines elements pairwise? But we don't have N-ary functions. We're still wrestling with this, but it doesn't look like anything will emerge until we have value types (> Java 9). – Stuart Marks Feb 18 '15 at 22:22
  • 1
    I'd definitely stick to a `BiFunction`, which can be `Pair::of` if necessary; strong -1 to tuple types. I'd be inclined to accept the limitation to the n=2 case, which can be expanded with currying if necessary -- that is, returning a stream of functions, to be zipped with a stream of arguments. – Louis Wasserman Feb 18 '15 at 22:24
  • @LouisWasserman Seems reasonable to **me**, but I accept that others would be dissatisfied with these limitations. Plus it would likely get in the way of improvements when and if value types come along. Meanwhile, streaming over an `IntRange` and using the values as indexes into multiple arrays/lists seems to satisfy a good chunk of use cases, though by no means all of them. – Stuart Marks Feb 19 '15 at 05:27
2

JDK doesn't provide the 'zip' API. But it can be done with third library abacus-common:

List<Integer> voterA = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> voterB = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> sumVotes = Stream.zip(voterA, voterB, (a, b) -> a + b).toList();

Disclosure: I'm the developer of abacus-common.

user_3380739
  • 1
  • 14
  • 14