In this second post about Java 9, we will focus on changes in the Collections and Streams API in the Java language. Examples can be downloaded via the following git repository: https://github.com/mydeveloperplanet/myjava9planet . The examples in the post below can be executed with JShell, the examples on GitHub contain the examples with unit tests.

Convenience factory methods for Collections

Factory methods are added to the Collections interfaces in order to create compact, immutable collection instances. The focus for these factory methods lies on small collections. Before Java 9, it was quite tedious to create such small collections. You had to write something like this (examples can be found in the NewCollections class):

Set<String> set = new HashSet<>();
set.add("apple");
set.add("lemon");
set.add("pineapple");
set = Collections.unmodifiableSet(set);

List/Set factory methods

In Java 9, factory methods for List and Set are available which take 0 up to 10 elements or varargs. The example above can now be rewritten as follows:

Set.of("apple", "lemon", "pineapple");

Pretty simple right? Now, you may wonder why 11 methods are made available why you also have a varargs method. The answer is because of performance. Also remember, that the focus for the usage lies on small collections.

Map factory methods

For Map, similar methods are available. Only difference is that they contain key/value pairs:

Map.of(1, "apple", 2, "lemon", 3, "pineapple");

The varargs variant for Map differs from the one for List/Set. It is called ofEntries and a list of Map entries must be provided:

Map.Entry<Integer, String> entry1 = Map.entry(1, "apple");
Map.Entry<Integer, String> entry2 = Map.entry((2, "lemon");
Map.Entry<Integer, String> entry3 = Map.entry((3, "pineapple");
Map.ofEntries(entry1, entry2, entry3);

Streams

The Streams interface API is extended with some useful methods. Examples can be found in the NewStreams class and the corresponding unit tests (the examples below can be executed with JShell).

dropWhile

The method dropWhile gives you the opportunity to read the stream from the moment the condition (predicate) matches. In the example below, the items from the stream are dropped until an item is found which matches the text ‘pineapple’. From that moment on, the items are processed.

Stream.of("apple", "lemon", "pineapple", "orange").dropWhile(s -> !s.equals("pineapple")).forEach(System.out::println);

The output is:

pineapple
orange

takeWhile

The method takeWhile is similar to dropWhile. The items of the stream are read up to the moment the condition (predicate) matches. In the example below, the items from the stream are processed until an item is found which matches the text ‘pineapple’.

Stream.of("apple", "lemon", "pineapple", "orange").takeWhile(s -> !s.equals("pineapple")).forEach(System.out::println);

The output is:

apple
lemon

As you can see, dropWhile starts from the item which matches the predicate (‘pineapple’ is included), takeWhile stops when an item matches the predicate (‘pineapple’ is not included).

Improved iteration

The new iterate method takes three arguments:

  1. seed: the initial element for the stream
  2. hasNext: the condition (predicate) which stops the iteration
  3. next: the next element

In the example we start to iterate from 0, after each iteration we increase the element by two until the value of 10 has been reached.

IntStream.iterate(0, x -> x < 10, x -> x + 2).forEach(System.out::println);

The output is:

0
2
4
6
8

ofNullable

With Java 9, the ofNullable method is introduced in the Streams API. How does this work?

First, we create a Customer class which returns a stream of fruits.

public class Customer {

  public Stream<String> fruits() {
    return Stream.of("apple", "lemon", "pineapple", "orange");
  }

}

Let’s use the same example of the dropWhile method from above.

Customer customer = new Customer();
Stream.of(customer).flatMap(Customer::fruits).dropWhile(s -> !s.equals("pineapple")).forEach(System.out::println);

The output is as expected the same as in the above example, namely:

pineapple
orange

What if customer is retrieved from a database or a third party library and that it could return null? We simulate this behavior by explicitly setting customer to null.

Customer customer = null;
Stream.of(customer).flatMap(Customer::fruits).dropWhile(s -> !s.equals("pineapple")).forEach(System.out::println);

As expected, the output is a NullPointerException 😦

When we rewrite the customer examples with ofNullable, the first example returns the same output as with the ‘of’-method.

Customer customer = new Customer();
Stream.ofNullable(customer).flatMap(Customer::fruits).dropWhile(s -> !s.equals("pineapple")).forEach(System.out::println);

Output:

pineapple
orange

The outcome of the example when customer is null, gives us a different result.

Customer customer = null;
Stream.ofNullable(customer).flatMap(Customer::fruits).dropWhile(s -> !s.equals("pineapple")).forEach(System.out::println);

No NullPointerException is thrown now, because the ofNullable method checked whether the customer object is null or not and returned an empty stream 🙂