We have been exploring Lambda expressions and Functional Interfaces in the past few posts. Now is the time to move to a related new feature called Streams.
The definition of Stream from the Oracle documentation is :
This kind of starts feeling like map reduce system operating inside JVM.
Now that we have seen a stream, how do we get one ?
In Map Reduce, any data stream comes from a data collection - a file. In JVM, the data is most commonly held in Java Collections. So the easiest way to get a stream is to start from a collection.
Accordingly I have this List of users which I would like to convert to a stream.
The stream method is defined in the Collection class and returns a sequential stream using the calling collection as the source:
Now that we have a stream, the next step would be to operate on it. I am going to first look at transformation operations available:
The output is as below:
The definition of Stream from the Oracle documentation is :
A sequence of elements supporting sequential and parallel aggregate operations.If I were to break this down into a picture
This kind of starts feeling like map reduce system operating inside JVM.
Now that we have seen a stream, how do we get one ?
In Map Reduce, any data stream comes from a data collection - a file. In JVM, the data is most commonly held in Java Collections. So the easiest way to get a stream is to start from a collection.
Accordingly I have this List of users which I would like to convert to a stream.
publicclass TestStreams {Now to get a stream.
staticclass Person {
String name;
long id;
int age;
public Person(String name, long id, int age) {
this.name = name;
this.age = age;
this.id = id;
}
}
staticfinalList<Person> persons = new ArrayList<Person>();
static {
persons.add(new Person("Robin", 12345, 27));
persons.add(new Person("Reena", 109876, 22));
persons.add(new Person("Meenakshi", 22245, 37));
persons.add(new Person("Nitin", 1511, 31));
}
}
publicstatic void display(List<Person> persons) {The output of the code is as below
Stream<Person> stream = persons.stream();
System.out.println("The Stream instance is " + stream);
System.out.println("No Of Elements in stream is " + stream.count());
}
The Stream instance is java.util.stream.ReferencePipeline$Head@6d06d69cAs seen here we called the stream method on the List to get a Stream instance.There is also a count method that returned the number of elements in the stream.
No Of Elements in stream is 4
The stream method is defined in the Collection class and returns a sequential stream using the calling collection as the source:
Now that we have a stream, the next step would be to operate on it. I am going to first look at transformation operations available:
publicstatic void booleanChecks(List<Person> persons) {The output is
boolean allAdults = persons.stream().allMatch(person -> person.age > 21);
System.out.println("All Passed users are adults ? " + allAdults);
boolean includesUsersWithNamesBeginningWithM = persons.stream().anyMatch(person -> person.name.startsWith("M"));
System.out.println("Any users whose name begins with M ? " + includesUsersWithNamesBeginningWithM);
boolean adminUser = persons.stream().noneMatch(person -> person.id == 1);
System.out.println("Is Admin Present ? " + adminUser);
}
All Passed users are adults ? trueThe above three methods take a Predicate as an instance and decide if the stream elements meet the condition. For example anyMatch() method returns true even if one of the elements in the sequence returns a true result for the passed predicate instance.The allMatch() method took each element in the sequence and evaluated the predicate returning a true, only if all the elements in the sequence satisfy the predicate. Conversely noneMatch() method will return true if the predicate fails for all elements in the stream.
Any users whose name begins with M true
Is Admin Present ? true
publicstatic void mapToOptional(List<Person> persons) {In this method, both operations return an instance of Optional. This is basically a container that may or may not include any element. The findAny method returns an Optional with a Person if any are present in the Stream. The findFirst method returns the first element it finds in the stream, else an Optional with no element.
Optional<Person> anyPerson = persons.stream().findAny();
System.out.println("findAny returned " + (anyPerson.isPresent() ? anyPerson.get().name : " null "));
Optional<Person> firstPerson = persons.stream().findFirst();
System.out.println("findFirst returned " + (firstPerson.isPresent() ? firstPerson.get().name : " null "));
}
The output is as below:
findAny returned RobinThe next methods look at value elimination from the streams
findFirst returned Robin
publicstatic void mapToStream(List<Person> persons) {The output is as below:
Stream<Person> distinctPersons = persons.stream().distinct();
System.out.println("Number of distinct users as per equals() method is " + distinctPersons.count());
System.out.println("Users over 30 are ");
Stream<Person> personsOver30 = persons.stream().filter(person -> person.age > 30);
personsOver30.forEach(person -> System.out.println(person.name + " is of age " + person.age));
System.out.println("Sorting the persons list");
Stream<Person> sortedPersons = persons.stream().sorted(Comparator.comparing(Person::getName));
sortedPersons.forEach(person -> System.out.println(person.getName()));
System.out.println("The persons list unsorted ");
persons.forEach(person -> System.out.println(person.getName()));
}
Number of distinct users as per equals() method is 4As can be seen here:
Users over 30 are
Meenakshi is of age 37
Nitin is of age 31
Sorting the persons list
Meenakshi
Nitin
Reena
Robin
The persons list unsorted
Robin
Reena
Meenakshi
Nitin
- The distinct method returns a new stream that contains only unique objects from the original string - uniqueness evaluated using the equals implementation.
- The filter method returns a stream based on the passed Predicate instance. It retains those stream members that pass the Predicate test.
- The sorted method returns a new stream which is the sorted version of the stream. As can be seen from the output, the new stream is a different stream. Our original stream stays unchanged.
publicstatic void mapToStream(List<Person> persons) {The output is as below:
Optional<Person> ascPerson1 = persons.stream().min(Comparator.comparing(Person::getName));
Optional<Person> descPerson1 = persons.stream().max(Comparator.comparing(Person::getName));
System.out.println("first Person is " + (ascPerson1.isPresent() ? ascPerson1.get().name : " null ")
+ "& last person is " + (descPerson1.isPresent() ? descPerson1.get().name : " null "));
System.out.println("2 Persons in list are : ");
Stream<Person> twoSizePersons = persons.stream().limit(2);
twoSizePersons.forEach(person -> System.out.println(person.getName()));
System.out.println("Person names in caps :: ");
Stream<Person> renamedPersonsStream = persons.stream().peek(
person -> person.name = person.name.toUpperCase());
renamedPersonsStream.forEach(person -> System.out.println(person.getName()));
System.out.println("After skipping, Persons in list are :: ");
Stream<Person> skippedPersons = persons.stream().skip(2);
skippedPersons.forEach(person -> System.out.println(person.getName()));
}
first Person is Meenakshi & last person is RobinI looked at some other methods related to stream:
2 Persons in list are :
Robin
Reena
Person names in caps ::
ROBIN
REENA
MEENAKSHI
NITIN
After skipping, Persons in list are ::
MEENAKSHI
NITIN
- The limit method of stream limits the size of the stream to the passed number. Here the first two elements were selected for the new Stream and the rest were not added to the Stream. This is quite like the LIMIT operator in SQL.
- The next is the peek method. This will return a new stream that contains all the elements in the original stream. Additionally the elements passed to the peek method are sent to a consumer. This will apply the operation before returning the new stream.
- The last is the skip method. This is the opposite of the limit method.It will skip the initial n elements and return the rest as a stream.