1

I have two collections:

List<String> names = ...
List<Long> counts = ...

I know that these two collections are equal and each value at position n in one list matches to the other list.

I want to perform a transformation where as an output I receive:

List<Foo> foos = ... 

where Foo is a record defined as:

record Foo(String name, long count)

For now this is my solution:

List<Foo> foos = IntStream.range(0, names.size())
                          .mapToObj(i -> new Foo(names.get(i), counts.get(i)))
                          .collect(Collectors.toList());

My question: Is there an other way to pair and collect Foo objects?

Forin
  • 1,549
  • 2
  • 20
  • 44
  • Please post your question on code review. – Ravindra Ranwala May 12 '20 at 07:30
  • 2
    @RavindraRanwala - That is really bad advice. Code review is not questions about how to do something differently. – Stephen C May 12 '20 at 07:32
  • 5
    Of course, there are other ways, e.g. you could use a loop. Regarding the Stream API, this is the simplest solution. Since it works and you don’t have any actual problems with it, don‘t bother thinking about alternatives. – Holger May 12 '20 at 07:34
  • 1
    See https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip – Sweeper May 12 '20 at 07:37
  • you are going right ! ..you can further use libraries that can all fancy your code. Useful Link https://www.baeldung.com/java-collections-zip – Aditya Rewari May 12 '20 at 08:16
  • The linked original question shows another way. However like @Holger I’d stay with the code you have if that were me. – Ole V.V. May 12 '20 at 16:03

1 Answers1

1

Your solution relies on the same size of both collections, so we should focus on this. Let's put the Java Stream API aside for a while since there is no other way traversing two collections simultaneously than using IntStream range with indices. Simply put, Java Stream API is not suitable for this use-case.

You need to ensure that no IndexOutOfBoundsException is thrown upon calling List::get. I prefer two ways:

  1. Two iterators with conjugated conditions:

    List<Foo> fooList = new ArrayList<>();
    while (namesIterator.hasNext() && countsIterator.hasNext()) {
        Foo foo = new Foo(namesIterator.next(), countsIterator.next());
        fooList.add(foo);
    }
    
  2. Using for-each iteration with indices up to the lower bound of both list sizes:

    int bound = Math.min(names.size(), counts.size());
    List<Foo> fooList = new ArrayList<>();
    for (int i=0; i<bound; i++) {
        Foo foo = new Foo(names.get(i), counts.get(i);
        fooList.add(foo);
    }
    

    .. which is similar to the Java Stream API way:

    List<Foo> fooList = IntStream.rangeClosed(0, Math.min(names.size(), counts.size()))
                                 .mapToObj(i -> new Foo(names.get(i), counts.get(i)))
                                 .collect(Collectors.toList());
    

There are also external libraries with dedicated methods to zipping such as Streams::zip from Guava or Seq::zip from jOOλ. The zipping mechanism is pretty much the same across libraries.

The current design of Java Stream API is not suitable for it.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183