Java 8 Streams

Reading Time: 5 minutes

Java Stream API was added in Java 8 in order to provide a functional approach to process a collection of objects. Do not get confused with I/O Streams, Java 8 Streams are a completely different thing.

Java Stream does not store data and is not a data structure. Also the underlying data source is not modified.

Java Stream uses functional interfaces and supports functional-style operations on streams of elements using lambda expressions.

Stream operations

Stream operations are divided into intermediate and terminal operations.

Intermediate operations are Stream operations that return a new Stream. These operations are used for producing new stream elements and to send the stream to the next operation. These operations are lazy, i.e. they are not executed until the result of the processing is needed.

List of all Stream intermediate operations:

  • filter()
  • map()
  • flatMap()
  • distinct()
  • sorted()
  • peek()
  • limit()
  • skip()

Terminal operations are Stream operations that do not return a new Stream. Once the terminal method is called in a stream, it consumes the stream and after that the stream can not be used anymore. Terminal operations are processing all the elements in the stream before they return the result.

List of all Stream terminal operations:

  • toArray()
  • collect()
  • count()
  • reduce()
  • forEach()
  • forEachOrdered()
  • min()
  • max()
  • anyMatch()
  • allMatch()
  • noneMatch()
  • findAny()
  • findFirst()

Some code examples

forEach

The simplest and most common operation, it loops over the elements of the stream, calling the supplied code on each element.

Map<Integer, List<User>> usersByAge = users.stream().collect(Collectors.groupingBy(User::getAge));  
usersByAge.forEach((age, u) -> System.out.format("age %s: %s\n", age, u)); 
// age 18: [John]   
// age 23: [Alex, David]   
// age 12: [Peter]

map

Produces new stream after applying a function to each element of the existing stream. It can produce a new stream of different type.

Integer[] userIds= { 5, 2, 7 }; 
List<User> users= Stream.of(userIds).map(employeeRepository::findById) 
.collect(Collectors.toList());   
assertEquals(users.size(), userIds.length);

collect

Repacks element to new data structure on data elements from the existing stream.

List<Employee> users= usersList.stream().collect(Collectors.toList()); 
assertEquals(usersList, users); 

filter

Produces new stream that contains elements from the existing stream that are fulfilling some criteria from the predicate.

users.stream().filter(user -> user.getAge() > 20) 
.collect().collect(Collectors.toList());

findFirst

Returns an Optional for the first entry in the stream.

users.stream().filter(user -> user.getAge() > 20).findFirst().orElse(null);

findAny()

Returns an Optional from any entry in the stream. It will most likely return the first entry in the stream in a non-parallel execution, but there is no guarantee for that.

users.stream().filter(user -> user.getAge() > 20).findAny().orElse(null);

toArray

Returns array from the stream.

User[] users = usersList.stream().toArray(User[]::new); 
assertThat(usersList.toArray(), equalTo(users));

flatMap

Used to flatten the data structure to simplify further operations on the stream. Useful for complex data structures.

List<String> names1 = Arrays.asList(“Peter”, “David”); 
List<String> names2 = Arrays.asList(“Dave”, “Robert”);  
List<String> names3 = Arrays.asList(“Tom”, “Tim”);  
List<List<String>> names = Arrays.asList(names1, names2, names3);  
List<String> flatNames = names.stream().flatMap(Collection::stream).collect(Colectors.toList());

peek

Performs the specified operation on each element of the stream and returns a new stream that can be used for further processing.

User[] arrayOfUsers = {  
new User(1, "James", 100000.0), 
new User(2, "Martin", 200000.0), 
new User(3, "David", 300000.0) }; 
List<User> userList = Arrays.asList(arrayOfUsers);  
userList .stream() 
.peek(e -> e.salaryIncrement(10.0))
.peek(System.out::println) 
.collect(Collectors.toList());   
assertThat(userList, contains(
hasProperty("salary", equalTo(110000.0)),  
hasProperty("salary", equalTo(220000.0)), 
hasProperty("salary", equalTo(330000.0))   
));

sorted()

Sorted is an intermediate operation which returns a sorted view of the stream. The elements are sorted in natural order unless you pass a custom Comparator.

List<String> names = Arrays.asList("Peter", "Dave", "Mike", "David", "Pete"); 
names.stream().sorted().map(String::toUpperCase).forEach(System.out::println);  
Output:
DAVE
DAVID
MIKE
PETE
PETER

Keep in mind that sorted does only create a sorted view of the stream without manipulating the ordering of the backed collection. The ordering of names is untouched.

count()

Count is a terminal operation returning the number of elements in the stream as a long.

long total = names.stream().filter((s) -> s.startsWith("D")).count(); 
System.out.println(total);  
Output: 2

reduce()

This terminal operation performs a reduction on the elements of the stream with the given function. The result is an Optional holding the reduced value.

List<Car> cars  = Arrays.asList(new Car("Kia", 19500), 
new Car("Hyundai", 20500),  
new Car("Ford", 25000),  
new Car("Dacia", 20000)); 

Optional<Car> car = cars.stream().reduce((c1, c2) 
-> c1.getPrice() > c2.getPrice() ? c1 : c2);  
car.ifPresent(System.out::println);  
Output: Ford 25000 

anyMatch()

The method accepts Predicate as an argument. This will return true once a condition passed as predicate satisfy. It will not process any more elements.

List<String> names = Arrays.asList("Peter", "Dave", "Mike", "David", "Pete"); 
boolean matched = names.stream().anyMatch(name -> name.startsWith("D"));  
System.out.println(matched);  
Output: true

allMatch()

The method accepts Predicate as an argument. The Predicateis applied to each entry in the stream and if every element satisfies the passed Predicate, then it returns true otherwise false.

List<String> names = Arrays.asList("Peter", "Dave", "Mike", "David", "Pete"); 
boolean matched = names.stream().allMatch(name -> name.startsWith("D")); 
System.out.println(matched);   
Output: false

noneMatch()

The method accepts Predicate as an argument. The Predicate is applied to each entry in the stream and if every element does not satisfy the passed Predicate, then it returns true otherwise false.

List<String> names = Arrays.asList("Peter", "Dave", "Mike", "David", "Pete"); 
boolean matched = names.stream().noneMatch(name -> name.startsWith("S"));
System.out.println(matched); 
Output: true

Conclusion

In this article we showed some of the details of the new Stream API in Java 8. We saw various operations and how lambdas are used to reduce a huge amount of boilerplate code.


JPoint Java conference in Moscow – 2019

Reading Time: 2 minutes

JPoint is one of the three (JPoint, Joker and JBreak) most common technical Java conferences for experienced Java developers. There will be 40 talks in two days, separated in 4 tracks in parallel. The conference takes place each year, this is being the seventh consecutive year.

Organization to visit JPoint conference

Apart from changing flights to reach Moscow everything else should not be any bigger issue. Book flights and choose some nearby hotel.

There are a few types of tickets. From which I’ll choose the personal ticket, main reason is the discount of 58%.

What is scheduled by now?

Many interesting subjects are going to be covered during two days of presentations:

  • New projects in Jenkins
  • Java SE 10 variable types
  • More of Java collections
  • Decomposing Java applications
  • AOT Java compilation
  • Java vulnerability
  • Prepare Java Enterprise application for production
  • Application migration JDK 9, 10, 11 and 12
  • Jenkins X

The following topics on the conference will be the most interesting ones for me:

  • Prepare Enterprise application for production (telemetry is crucial).
  • Is Java so vulnerable? What can we do to reduce security issues?
  • What is the right way of splitting application to useful components?
  • It looks that now with Jenkins Essentials there is significant less overhead for managing it, without any user involvement. Let us see what Jenkins replaced with few commands.

Just half of presentations are scheduled by now. Expect many more to be announce.