Java 8 Streams & Lambda

One of my stories has been to convert all for loops in a Board class for my tic tac toe with streams. At first I thought this would be very simple, why wouldn’t it be? We already have the code so we just have to convert it! I was wrong. Very wrong.

There is not a lot of good documentation on Java 8 streams, and I found it very hard understanding source code of the streams as it looks like another language. I started off with the easiest conversion:

public ArrayList availablePositions() {
ArrayList<Integer> positions = new ArrayList<>();
for (int i=0; i < boardSize(); i++) {
if (state.charAt(i) == '-') {
positions.add(i + 1);
}
}
return positions;
}

This became:

public List<Integer> availablePositions() {
return range(0, boardSize())
.filter(p -> state.charAt(p) == '-')
.mapToObj(i -> i + 1)
.collect(toList());
}

So range is similar to a normal for loop:

range(0, boardSize())

Is the same as saying:

for(int i=0; i < boardSize(); i++)

With range you can not choose how much it increments by though. And then I used .mapToObj and .collect to create and store the data (I talk more about this later).

So that was fairly simple, and then I moved on to a nested loop:

public ArrayList<String> getAllColumns() {
ArrayList<String> columns = new ArrayList<>();
for (int i=0; i < dimension; i++) {
String column = "";
for (int j=i; j < i + boardSize(); j+= dimension) {
column += state.charAt(j);
}
columns.add(column);
}
return columns;
}

Became:

public List<List<String>> getAllColumns() {
return range(0, dimension)
.mapToObj(i -> getColumnCells(i))
.collect(toList());
}

So to avoid creating a nested stream inside of a stream and creating a mess, I implemented this method:

public List<String> getColumnCells(int index) {
String[] test = state.split("");
ArrayList<String> list = new ArrayList<>(Arrays.asList(test));
return range(0, dimension)
.mapToObj(i -> list.get(index + i * dimension))
.collect(toList());
}

Which gets all the cells on the column of the index, so its no longer getAllColumns responsibility to get the cell values. What mapToObj does, is it returns an object valued stream consisting of the results of applying the given function to the elements of the stream. So in my example, I have an anonymous function which gets each value from a row and then is collected using .collect and toList() to put the data into a list before collecting.

Something I found confusing was ->, this is an Lambda expression which is an anonymous function (a function with out a name). I saw it written in another way which helped me understand it a lot more. Here it is:

public List<String> getColumnCells(int index) {
String[] test = state.split("");
ArrayList<String> list = new ArrayList<>(Arrays.asList(test));
return range(0, dimension)
.mapToObj(i -> {
return list.get(index + i * dimension);
})
.collect(toList());
}

So as you can see its just the body of a method and I am returning a value which is then collected.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.