I’ve previously looked at how to work on a higher level by replacing loops with streams. In this post I want to continue this by looking at IntStream
and how it could replace the traditionalfor (int i=0;...
loop.
IntStream
is a stream of primitive int values. It’s part of the java.util.stream
package, and you can find a lot of the same functions there as in the normal Stream<T>
. There also exists streams for dealing with double and long primitives, but we’ll leave that out since it acts in pretty much the same way.
Creating the IntStream
There are several ways of creating an IntStream
.
Let’s start by looking at the of
function.
IntStream.of(1, 2, 3);
// > 1, 2, 3
of
just takes an arbitrary number of ints, creating an IntStream
based on them.
Usually we don’t want to list up all the ints like this, so let’s instead continue by looking at two functions that probably will be used most of the time.
IntStream.range(1, 3);
// > 1, 2
IntStream.rangeClosed(1, 3);
// > 1, 2, 3
With range
and rangeClosed
, we define a range of ints that we want to create a stream of.
As you probably guessed from the resulting streams, the rangeClosed
function includes the ending int, while range
excludes it.
Now what if we don’t want all the ints within a range? What if we just want every even number?
Then we could use the iterator
function.
IntStream.iterate(0, i -> i + 2).limit(3);
// > 0, 2, 4
With iterator
we can define a start value and a function that will calculate the next ints based on the previous element.
iterator
creates an infinite stream, so I’ve used limit
to create a stream containing just three elements.
The last function we’re going to cover for creating IntStreams
, is the generate
function.
IntStream.generate(() -> ThreadLocalRandom.current().nextInt(10)).limit(3);
// > 4, 1, 7
generate
looks a lot like iterator
, but differ by not calculating the ints based on the previous element.
It simply takes an IntSupplier
that will independently calculate the next int.
Working with the ints
Now that we have seen how we can create an IntStream
, let’s start to play with it by using some of the functions it offer.
Let’s start by looking at the different map functions
First, let’s use the normal map
function to find the squared value of all ints between 0 and 5.
IntStream.range(1, 5).map(i -> i * i);
// > 1, 4, 9, 16
The map
function maps to an IntStream
, so what about when we want to return other types of streams?
Well, then we have dedicated map functions to handle these scenarios
Stream<Color> stream = IntStream.range(1, 5).mapToObj(i -> getColor(i));
mapToObject
will simply return a Stream
of the type that the mapping returns.
Now, if you just want to convert an IntStream
to a Stream<Integer>
, there’s a dedicated function for this job called boxed
.
Stream<Integer> stream = IntStream.range(1, 5).boxed();
You’ll also find map functions that returns DoubleStream
and LongStream
.
DoubleStream stream = IntStream.range(1, 5).mapToDouble(i -> i);
LongStream stream = IntStream.range(1, 5).mapToLong(i -> i);
Moving on, let’s try out some of the matching functions
Let’s start by using matchAny
to confirm that a certain range contains at least one even number.
IntStream.range(1, 5).anyMatch(i -> i % 2 == 0);
// > true
matchAny
will check if a predicate holds for at least one of the elements in the stream.
We also have two other match functions, matchNone
and matchAll
, that should return false for this predicate.
IntStream.range(1, 5).allMatch(i -> i % 2 == 0);
// > false
IntStream.range(1, 5).noneMatch(i -> i % 2 == 0);
// > false
Great!
Let’s now use the filter
function to filter all the even numbers, then confirm that the filter did it’s job by using allMatch
to make sure all numbers are even. Let’s also use noneMatch
to make sure we can’t find any odd numbers.
IntStream.range(1, 5)
.filter(i -> i % 2 == 0)
.allMatch(i -> i % 2 == 0);
// > true
IntStream.range(1, 5)
.filter(i -> i % 2 == 0)
.noneMatch(i -> i % 2 != 0);
// > true
IntStream
contains functions for fetching the max and min value
IntStream.range(1, 5).max().getAsInt();
// > 4
IntStream.range(1, 5).min().getAsInt();
// > 1
Both these methods and a lot of the other functions that returns one element, will return anOptionalInt
.
OptionalInt
, like Optional
, gives some great higher-order functions that are abstracting away the null checks. Since that’s not the topic of this post, we’ll just fetch the int value assuming it’s there.
IntStream
also offers the excellent reduce
function
IntStream.range(1, 5).reduce(1, (x, y) -> x * y)
// > 24
Here we reduce the stream by multiplying all the elements.
Concurrency
Another great thing about the Stream API, is that you can work on elements in parallel.
Let’s say we have a heavy operation that we want to execute four times.
IntStream.range(1, 5).parallel().forEach(i -> heavyOperation());
parallel
will execute heavyOperation
in parallel for all the elements in the stream. Thanks to thisparallel
, we can potentially save a great amount of time, with very little effort.