Over

120,000

Worldwide

Saturday - Sunday CLOSED

Mon - Fri 8.00 - 18.00

Call us

 

Java 8 Stream operations

Java 8 Stream operations

filter(), map() and flatMap()

Streams were added to Java starting from Java 8, from where we get the lambdas, functional interfaces etc. which open a great way of using powerful techniques.

The Streams should not be confused with Java IO streams, cause these have very little to do with each other.

The java.util.stream package has classes, interfaces and types for allowing functional-style operations over elements. Stream allows us without writing much special code to rely on multi-core architecture. A Stream can be defined for any kind of object, a stream of numbers or characters etc. as it is a typed interface, stream of T.

We might think that it looks like Collections, but it has not so much to do with Collections. For example, one of the differences between them, is that a Stream does not hold any data, in that sense, is not a data structure, whereas there is data held in Collection. Stream just processes the data and pulls it out from the source, which might be an array or a Collection or something else. This is very important to understand and remember.

Now we can view some examples of Streams and its usages. And after that we can see its benefits and the advantages of using it.

Filter

Let us look at filter method.

Now we have a List of letters.

public class FilterExample {

public static void main(String[] args) {

List<String> letters = Arrays.asList(“A”, “B”, “C”, “D”);

for (String letter : letters) {

if (!letter.equals(“D”)) {

System.out.println(letter + ” “);

}

}

letters.stream()

.filter(letter -> !letter.equals(“D”))

.forEach(letter -> System.out.println(letter));

}

}

Here we have a simple for-each that we know.

But let us get familiar with a new approach that filter the objects which match to the given predicate, into a new Stream that is returned by the method.

public class FilterExample2 {

public static void main(String[] args) {

List<String> letters = Arrays.asList(“A”, “B”, “C”, “D”);

letters.stream()

.filter(FilterExample2::isNotC)

.forEach(System.out::println);

}

private static boolean isNotC(String letter) {

return !letter.equals(“C”);

}

}

So here we see that the Streams look easier and better to use with the Lambda expressions, which we also saw with an example compared to foreach approach.

Map

Now we have a List of animals. And want to iterate through that List.

List<String> animals = Arrays.asList(“dog”, “cat”, “fish”, “pig”);

for (String animal : animals) {

if (!animal.equals(“fish”)) {

Animal type = new Animal(animal);

System.out.println(type);

}

}

This is the simple iteration we know.

Now we can see the iteration with another way, by using map method.

animals.stream()

.filter(animal -> !animal.equals(“fish”))

.map(Animal::new)

.forEach(System.out::println);

And here is the Animal class that we have used above.

class Animal {

private String type;

public Animal(String type) {

this.type = type;

}

@Override

public String toString() {

return “type='” + type + “‘”;

}

}

So this was one example of map method, other examples can be converting a list of integers and then the square of each number.

FlatMap

So, now we already know how to transform objects of a stream into another type of objects by using map operation. With map we can map every object to exactly one another object. But if we want to transform one object into several others, we can use this flatMap operation.

This method transforms each of the elements of stream to a stream of other objects. And in the end the content of the stream will be placed in the returning stream of flatMap method. Here we can look at this example:

public class FlatMapDemo {

public static void main(String[] args) {

List<String> cars = Arrays.asList(“BMW”, “Ford”, “Mercedes”, “Kia”); List<String> fruits = Arrays.asList(“apple”, “grape”, “peach”);

List<String> colors = Arrays.asList(“red”, “blue”, “black”);

List<List<String>> allLists = new ArrayList<List<String>>();

allLists.add(cars);

allLists.add(fruits);

allLists.add(colors);

List<String> listOfAllLists = new ArrayList<String>();

for (List<String> singleList : allLists) {

for (String hName : singleList) {

listOfAllLists.add(hName);

}

}

System.out.println(listOfAllLists);

//lambda expression

List<String> flatMapList = allLists.stream().flatMap(hList ->

hList.stream()).collect(Collectors.toList());

//method reference

List<String> flatMapList2 =

allLists.stream().flatMap(Collection::stream).collect(Collectors.toList());

System.out.println(flatMapList);

System.out.println(flatMapList2);

}

}

So, we see that the map method wraps the underlying sequence in a Stream, but the flatMap method is used to flatten a Stream of collections to a Stream of objects.

Here is another example.

This is just a simple Pupil class:

class Pupil {

public String name;

public String grade;

public List<String> subjects;

public Pupil(String name, String grade, List<String> subjects) {

this.name = name;

this.grade = grade;

this.subjects = subjects;

}

public String getName() {

return name;

}

public String getGrade() {

return grade;

}

public List<String> getSubjects() {

return subjects;

}

}

And here is a List of Pupils:

List<Pupil> pupilsList = new ArrayList<>();

pupilsList.add(new Pupil(“Tom”, “5th grade”, Arrays.asList(“math”, “history”, “science”))); pupilsList.add(new Pupil(“Ann”, “2nd grade”, Arrays.asList(“algebra”, “math”))); pupilsList.add(new Pupil(“Mary”, “7th grade”, Arrays.asList(“biology”, “geology”)));

List<String> courses = pupilsList.stream()

.flatMap(pupil -> pupil.getSubjects().stream()).collect(Collectors.toList());

System.out.println(courses);

And the output will be:

[math, history, science, algebra, math, biology, geology]

Using different methods of Streams we see many of its advantages. The Streams can briefly express rather complex behaviour that the developer is coding. And the Streams encourage less mutability, and the types of programs written through Streams are usually programs where you do not modify objects.

Streams are often more concise and it is better for understanding. Using the Streams you have a strong affinity with functions. This is the most convenient way to apply any function to the sequence of objects.

Leave a Reply

Your email address will not be published. Required fields are marked *

Working Hours

  • Monday 9am - 6pm
  • Tuesday 9am - 6pm
  • Wednesday 9am - 6pm
  • Thursday 9am - 6pm
  • Friday 9am - 6pm
  • Saturday Closed
  • Sunday Closed