Search code examples
javajava-8primitiveboxingjava-stream

Using a Collector on a primitive stream


Is there in Java 8 any way to use Stream::collect(Collector) on primitive streams?

Normally a Stream<Integer> as example has two methods for collecting:

However IntStream only has one method for collecting:

Now as example code I have the following:

@Override
public void run() {
    result = LongStream.range(1, maximum).boxed()
            .collect(Collectors.toMap(i -> i, i -> (int)Iterators.longStream(new CollatzGenerator(i)).count()))
            .entrySet().stream()
            .max(Comparator.comparingLong(Map.Entry::getValue))
            .get().getKey();
}

As you can see I first box the primitives in order to be able to use a Collectors. method.

Is there any way I can use primitives and still have the same code with Collectors.toMap ?


Solution

  • Since Map is a Generic interface there is no way to create a Map without boxing. However, it doesn’t make sense to collect items into a Map at all when all you want is to create another stream (with just two values wrapped in a Map.Entry). You can just create the Map.Entry instances without collecting the values:

    LongStream.range(1, maximum)
      .mapToObj(i->new AbstractMap.SimpleEntry<>(i, Iterators.longStream(new CollatzGenerator(i)).count()))
      .max(Comparator.comparingLong(Map.Entry::getValue))
      .get().getKey();
    

    This still does auto-boxing but once you are at this point you can get rid of the Map.Entry too by creating an appropriate value holder class by yourself:

    static final class TwoLongs {
        final long key, value;
        TwoLongs(long k, long v) { key=k; value=v; }
        public long getKey() { return key; }
        public long getValue() { return value; }
    }
    

    With this holder class you can process your data without boxing the longs:

    LongStream.range(1, maximum)
      .mapToObj(i->new TwoLongs(i, Iterators.longStream(new CollatzGenerator(i)).count()))
      .max(Comparator.comparingLong(TwoLongs::getValue))
      .get().getKey();
    

    Well, it is still some kind of boxing but creating one item (the TwoLongs instance) object instead of three (one Map.Entry and two Longs).