Java Streams
Stream is an new abstraction that lets one process data in a declarative way.
A stream does not store data and, in that sense, is not a data structure. It also never modifies the underlying data source.
The easiest way to learn a new API or a construct is by trying it. So here is an exercise that I tried to learn the Stream API. This is an exercise from exercism.io.
The problem is the famous grains
problem doubling the number of grains on each square of the chess which has 64 squares (8×8).
I started of by keeping things simple having a LinkedHashMap
that stores pre-initalized
square and number of grains
.
private Map<Integer, BigInteger> lHM = new LinkedHashMap<>();
private void Initialize() {
for(int i=0; i<64; i++) {
if (i == 0) {
lHM.put(Integer.valueOf(1), BigInteger.ONE);
continue;
}
lHM.put(i+1, lHM.get(i).multiply(BigInteger.TWO));
}
}
lHM
defined is initalized for all the possible grain permutations for a 64 square board.
lHM.put(i+1, lHM.get(i).multiply(BigInteger.TWO));
E.g.
- For every square > 1 we are putting
Grains in the previous square * 2
This is very simple version of and had to be improved so time to use Streams.
import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.IntStream;
/**
* Grains on a board.
* 1 + 2 + 6 + 18 + .......
*/
class Grains {
BigInteger grainsOnSquare(final int square) {
if (square < 1 || square > 64) {
throw new IllegalArgumentException("square must be between 1 and 64");
}
return BigInteger.TWO.pow(square - 1);
}
BigInteger grainsOnBoard() {
return IntStream.range(1, 65).mapToObj( i -> grainsOnSquare(i)).reduce(BigInteger::add).get();
}
}
I did away with the data-structure and resorted to use simple Streams – IntStream
.
For the method
grainsOnSquare
I have used pow
method of BigInteger which simple calculates 2^(Square – 1)
- 1st Square 2^0 = 1
- 2nd Square 2^1 = 2
- …. 4th Square 2^3 = 8
Now it is easier to read and optimised. How about the total grains here is where the streams come in handy for me as I do not have a DS storing any values.
IntStream.range(1, 65).mapToObj( i -> grainsOnSquare(i)).reduce(BigInteger::add).get();
I used the range()
method to iterate between 1-64 as the second parameter is exclusive, and then for each square i
I got the grainsOnSquare
which was reduced using BigInteger.add
and the final value is returned.
References
- https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
- https://www.oracle.com/technical-resources/articles/java/ma14-java-se-8-streams.html
- https://docs.oracle.com/javase/tutorial/collections/intro/index.html