Streams of primitives
The generic Stream<T> class is used to process objects which are always represented by reference types. For example, to work with integers, it’s possible to create Stream<Integer> that wraps primitive int’s into the Integer class. But this is not an efficient way to work with integers, since it needs additional wrapper objects. Fortunately, there are three primitive specialized types called IntStream, LongStream, and DoubleStream which can effectively process primitive values without extra boxing.
There is no
CharStream,ByteStream,ShortStream,FloatStreamandBooleanStream.
Creating primitive type streams
There are lots of ways to create primitive type streams. Some of the ways are suitable for all streams while others are not.
- Passing elements in the
ofmethod:IntStream ints = IntStream.of(1, 2, 3); LongStream longs = LongStream.of(1, 2, 3); DoubleStream doubles = DoubleStream.of(12.2, 18.1);This looks quite similar to collections. It is also possible to create an empty stream invoking
IntStream.of()orIntStream.empty(). - From an array of primitives:
IntStream numbers = Arrays.stream(new int[]{1, 2, 3});This way works for all types of primitive specialized streams. It is also possible to specify
start(inclusive) andend(exclusive) positions to create astreamonly from a sub-array. - For
IntStreamandLongStreamit’s possible to invokerange()andrangeClosed()to create streams from ranges.IntStream numbers = IntStream.range(10, 15); // from 10 (incl) to 15 (excl) LongStream longs = LongStream.rangeClosed(1_000_000, 2_000_000); // it includes both bordersThe difference is the method
rangeClosedincludes its upper bound while range does not. - Getting
IntStreamfrom a string:IntStream stream = "aibohphobia".chars(); // It returns IntStream!This only works for
IntStreamsince characters can be represented asint’s. -
Other ways to create primitive type streams are the same as for generic streams:
generate,iterate,concatand so on. Here is an example of generatingDoubleStreamwith ten random numbers and printing them:DoubleStream.generate(Math::random) .limit(10) .forEach(System.out::println);
Additional operations
The primitive streams have all the same methods as generic streams, but their methods accept primitive specialized functions as arguments. For example the forEach method of IntStream takes IntConsumer, but not Consumer<Integer>. Fortunately, this does not affect the possibilities of the streams.
There are a few additional aggregating operations such as min, max, average and sum. The first three return an optional object which represents a result or nothing since the initial stream can be empty.
Note, actually Stream<T> also provides min and max but its methods need a comparator as the argument. The following code demonstrates the methods:
int[] numbers = { 10, 11, 25, 14, 22, 21, 18 };
int max = IntStream.of(numbers).max().getAsInt();
System.out.println(max); // 25
int min = IntStream.of(numbers).min().getAsInt();
System.out.println(min); // 10
double avg = IntStream.of(numbers).average().orElse(0.0);
System.out.println(avg); // 17.2857...
int sum = IntStream.of(numbers).sum();
System.out.println(sum); // 121
It is also possible to calculate these aggregates at once using a single invocation of the summaryStatistics method.
IntSummaryStatistics stat = IntStream.rangeClosed(1, 55_555).summaryStatistics();
System.out.println(String.format("Count: %d, Min: %d, Max: %d, Avg: %.1f",
stat.getCount(), stat.getMin(), stat.getMax(), stat.getAverage()));
Here are the results:
Count: 55555, Min: 1, Max: 55555, Avg: 27778.0
Transforming streams
You can perform various transformations of primitive type streams.
-
Transforming a primitive type stream to another one using
asDoubleStream()forIntStreamandLongStream, orasLongStreamforIntStreamonly. Here is an example which converts a stream of integers into a stream of doubles:IntStream.of(1, 2, 3, 4) .asDoubleStream() .forEach(System.out::println); // it prints doubles 1.0, 2.0, ... - Transforming a primitive type stream into the generalized stream using the
boxed()method (i.e.,IntStream→Stream<Integer>). All the primitive type streams have this.Stream<Integer> streamOfNumbers = IntStream.range(1, 10).boxed(); - Transforming a generalized stream into a stream of primitives can be done invoking one of
mapToInt(),mapToLong()ormapToDouble()methods with thei -> ilambda expression as the argument:List<Integer> numbers = List.of(1, 5, 9); int sum = numbers.stream().mapToInt(i -> i).sum(); // 15This can be especially useful when you want to invoke one of the specific primitive stream’s methods.
Summary
We’ve considered three primitive specialized streams which are similar to the generalized stream but have some features:
- their methods take primitive specialized functions as arguments;
- they have additional methods such as
range,sum,average,summaryStatisticsand some others; - they have the performance benefits since there is no need to perform boxing/unboxing operations;
- they can be converted in the generalized stream and vice versa.