© Fu Cheng 2018

Fu Cheng, Exploring Java 9, https://doi.org/10.1007/978-1-4842-3330-6_4

4. Collections, Stream, and Optional

Fu Cheng

(1)Auckland, New Zealand

This chapter covers topics related to Java 9 changes in collections, Stream, and Optional.

Factory Methods for Collections

Java 9 adds some convenient methods you can use to create immutable collections.

The List.of() Method

The new static method List.of() has a number of overloads that you can use to create immutable lists from zero or many elements; see Listing 4-1.

Listing 4-1. Example of List.of()
List.of();
List.of("Hello", "World");
List.of(1, 2, 3);

List.of() doesn’t allow null values. Passing null to it results in NullPointerException.

The Set.of() Method

The new static method Set.of() also has a number of overloads for creating immutable sets from zero or many elements; see Listing 4-2.

Listing 4-2. Example of Set.of()
Set.of();
Set.of("Hello", "World");
Set.of(1, 2, 3);

Because sets don’t allow duplicate elements, passing duplicate elements to Set.of() causes it to throw IllegalArgumentException. Set.of() doesn’t allow null values. Passing null to it results in NullPointerException.

The Map.of() and Map.ofEntries() Methods

Two new static methods, Map.of() and Map.ofEntries(), are used to create immutable maps with zero or many entries.

Map.of() has 11 different overloads for creating maps with zero to ten entries; see Listing 4-3. Keys and values are specified in pairs.

Listing 4-3. Example of Map.of()
Map.of(); // empty map
Map.of("Hello", 1, "World", 2); // -> Map<String, Integer> with two entries

Map.ofEntries() can create maps from any number of Map.Entry objects; see Listing 4-4.

Listing 4-4. Example of Map.ofEntries()
Map.ofEntries(
    new AbstractMap.SimpleEntry<>("Hello", 1),
    new AbstractMap.SimpleEntry<>("World", 2)
);

Map.of() and Map.ofEntries() don’t allow null keys or values. Passing duplicate keys causes them to throw IllegalArgumentExceptions. Passing null to them results in NullPointerExceptions.

Arrays

This section discusses the methods that have been added to java.util.Arrays in Java 9.

Mismatch() Methods

The group of mismatch() methods finds and returns the index of the first mismatch between two arrays. mismatch() returns -1 if there is no mismatch. There are different overloads for boolean, byte, char, double, float, int, long, short, and Object arrays. You can also specify the start and end index in both arrays for checking the mismatch. For object arrays, you can also specify a java.util.Comparator object for the comparison. In Listing 4-5, you can see different use cases of mismatch() being tested.

Listing 4-5. Example of mismatch()
@Test
public void testMismatch() throws Exception {
  assertEquals(0, Arrays.mismatch(new int[]{1}, new int[]{2}));
  assertEquals(1, Arrays.mismatch(new int[]{1}, new int[]{1, 2}));
  assertEquals(1, Arrays.mismatch(new int[]{1, 3}, new int[]{1, 2}));
  assertEquals(-1, Arrays.mismatch(
      new int[]{1, 3}, 0, 1,
      new int[]{1, 2}, 0, 1));
}

Compare() Methods

The group of compare()methods compares two arrays lexicographically. There are different overloads for boolean, byte, char, double, float, int, long, short, and Object arrays. You can also specify the start and end index in both arrays for comparison. For object arrays, you can also specify a java.util.Comparator object for the comparison. Listing 4-6 tests different use cases of compare().

Listing 4-6. Example of compare()
@Test
public void testCompare() throws Exception {
  assertEquals(0, Arrays.compare(new int[]{1}, new int[]{1}));
  assertTrue(Arrays.compare(new int[]{0}, new int[]{1}) < 0);
  assertTrue(Arrays.compare(new int[]{1}, new int[]{0}) > 0);
  assertEquals(0, Arrays.compare(
      new int[]{1, 3}, 0, 1,
      new int[]{1, 2}, 0, 1));
}

Equals() Methods

The group of equals() methods returns true if two arrays are equal to each other. There are different overloads for boolean, byte, char, double, float, int, long, short, and Object arrays. You can also specify the start and end index in both arrays for an equality check. For object arrays, you can also specify a java.util.Comparator object for the comparison. Listing 4-7 tests different use cases of equals().

Listing 4-7. Example of equals()
@Test
public void testEquals() throws Exception {
  assertTrue(Arrays.equals(new int[]{1}, new int[]{1}));
  assertTrue(Arrays.equals(
      new int[]{1, 2}, 0, 1,
      new int[]{1, 3}, 0, 1));
}

Stream

This section covers the new methods that have been added to java.util.stream.Stream in Java 9.

The ofNullable() Method

The static method Stream<T> ofNullable(T t) returns a Stream of zero or one element, depending on whether the input value is null. Listing 4-8 shows examples of this method.

Listing 4-8. Example of Stream.ofNullable()
@Test
public void testOfNullable() throws Exception {
  assertEquals(1, Stream.ofNullable("").count());
  assertEquals(0, Stream.ofNullable(null).count());
}

The dropWhile() Method

The method Stream<T> dropWhile(Predicate<? super T> predicate) drops elements in the stream until it encounters the first element that doesn’t match the predicate. If the stream is ordered, then the longest prefix of elements that matches the given predicate is dropped. If the stream is unordered, and some (but not all) elements of this stream match the given predicate, then it’s undetermined to tell which elements are dropped. However, if all elements of the stream match the given predicate, or no elements of the stream match the given predicate, then it doesn’t matter whether the stream is ordered or unordered; the result is determined.

In Listing 4-9, the stream contains elements from 1 to 5. The predicate checks if the element is odd. The first element 1 is dropped. The result stream contains four elements.

Listing 4-9. Example of Stream.dropWhile()
@Test
public void testDropWhile() throws Exception {
  final long count = Stream.of(1, 2, 3, 4, 5)
      .dropWhile(i -> i % 2 != 0)
      .count();
  assertEquals(4, count);
}

The takeWhile() Method

The method Stream<T> takeWhile(Predicate<? super T> predicate) performs the opposite action on a stream that dropWhile() does. takeWhile() takes elements in the stream until it encounters the first element that doesn’t match the predicate. takeWhile() has the same behavior as dropWhile() when it processes ordered and unordered streams.

In Listing 4-10, when the stream is processed as in Listing 4-6 using takeWhile(), the resulting stream only has one element of value 1.

Listing 4-10. Example of Stream.takeWhile()
@Test
public void testTakeWhile() throws Exception {
  final long count = Stream.of(1, 2, 3, 4, 5)
      .takeWhile(i -> i % 2 != 0)
      .count();
  assertEquals(1, count);
}

The iterate() Method

The static method Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) is a new way to generate streams using the iterator pattern. seed is the initial element for iteration. hasNext is the predicate that applies to the current element to check if the stream should be terminated. next is the function that applies to the current element to produce the next element in the stream. If the initial element seed doesn’t match the predicate hasNext, the result stream is empty.

In Listing 4-11, the result stream uses 2 as the initial element and increases the current value by 2 to produce the next value. The stream terminates when the value is greater than 10. There will be 5 elements in the stream.

Listing 4-11. Example of Stream.iterate()
@Test
public void testIterate() throws Exception {
  final Stream<Integer> stream =
      Stream.iterate(2, i -> i <= 10, i -> i + 2);
  assertEquals(5, stream.count());
}

IntStream, LongStream, and DoubleStream

Methods dropWhile(), takeWhile(), and iterate() are also added to IntStream, LongStream, and DoubleStream.

Collectors

Several new methods have also been added to java.util.stream.Collectors in Java 9.

The filtering() Method

The method <T, A, R> Collector<T, ?, R> filtering(Predicate<? super T> predicate, Collector<? super T,A,R> downstream) filters input elements by applying the predicate function, and it only accumulates elements to the downstream Collector when the predicate returns true.

Listing 4-12 shows how to use filtering() to filter a stream of Strings by its length and collect them into a Set. This actually can be simplified to use Stream.filter() first and then collect the Strings into a Set.

Listing 4-12. Simple Example of Collectors.filtering()
@Test
public void testSimpleFiltering() throws Exception {
  final Set<String> result = Stream.of("a", "bc", "def")
      .collect(Collectors.filtering(
          v -> v.length() > 1,
          Collectors.toSet()));
  assertEquals(2, result.size());
}

Listing 4-13 shows a more complicated example of filtering(). The variable users is a map of users’ names to their ages. This example uses groupingBy() to group the map entries by the first character of the name. Then it used filtering() to only keep entries with ages greater than 18. Finally, it uses mapping() to map the entries into the names. The result is a map of each name’s first character to the Set of names. Because the age of Bob is only 16, the value of key B in the result map is an empty Set.

Listing 4-13. Complicated Example of Collectors.filtering()
@Test
public void testComplicatedFiltering() throws Exception {
  final Map<String, Integer> users = Map.of(
      "Alex", 30,
      "Bob", 16,
      "David", 50
  );
  final Map<String, Set<String>> result = users
      .entrySet()
      .stream()
      .collect(Collectors.groupingBy(entry -> entry.getKey().substring(0, 1),
          Collectors.filtering(entry -> entry.getValue() > 18,
              Collectors.mapping(
                  Map.Entry::getKey,
                  Collectors.toSet()))));
  assertEquals(1, result.get("A").size());
  assertEquals(0, result.get("B").size());
  assertEquals(1, result.get("D").size());
}

The flatMapping() Method

The method <T, U, A, R> Collector<T, ?, R> flatMapping(Function<? super T,? extends Stream<? extends U>> mapper, Collector<? super U,A,R> downstream) applies a flat mapping function to the input elements and accumulates elements in the mapped streams to the downstream Collector.

In Listing 4-14, given a stream of Strings, you can see flatMapping() used to map a String to a stream of Integers and then collect all Integers into a Set. The result Set only contains three elements.

Listing 4-14. Example of Collectors.flatMapping()
@Test
public void testFlatMapping() throws Exception {
  final Set<Integer> result = Stream.of("a", "ab", "abc")
      .collect(Collectors.flatMapping(v -> v.chars().boxed(),
          Collectors.toSet()));
  assertEquals(3, result.size());
}

Optional

Three new methods have been added to java.util.Optional.

The ifPresentOrElse() Method

The method void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) performs the given action if the value is present, otherwise it performs the given action emptyAction.

In Listing 4-15, the method checkValue() increases or decreases the count based on whether the value is present. The first invocation of checkValue() decreases the count, whereas the second invocation increases the count.

Listing 4-15. Example of Optional.ifPresentOrElse()
public class OptionalTest {

  private AtomicInteger count;

  @Before
  public void setUp() throws Exception {
    this.count = new AtomicInteger();
  }


  @Test
  public void testIfPresentOrElse() throws Exception {
    checkValue(Optional.empty());
    assertEquals(-1, this.count.get());
    checkValue(Optional.of(1));
    assertEquals(0, this.count.get());
  }


  private void checkValue(final Optional<Integer> value) {
    value.ifPresentOrElse(
        v -> this.count.incrementAndGet(),
        () -> this.count.decrementAndGet()
    );
  }
}

The Optional.or() Method

The method Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) returns an Optional with the value if a value is present, otherwise it returns an Optional produced by the supplier function. Listing 4-16 shows the example of this method.

Listing 4-16. Example of Optional.or()
@Test
public void testOr() throws Exception {
  assertTrue(Optional.empty().or(() -> Optional.of(1)).isPresent());
}

The stream() Method

The method Stream<T> stream() converts the Optional into a Stream. If a value is present, then the result stream contains only the value, otherwise the result stream is empty. This method is useful when you’re working with flatMap() to convert a Stream of Optionals into a Stream of present value elements.

In Listing 4-17, the stream of Optionals contains three elements, but only two of them have values. After using flatMap(), the result stream contains the two present values.

Listing 4-17. Example of Optional.stream()
@Test
public void testStream() throws Exception {
  final long count = Stream.of(
      Optional.of(1),
      Optional.empty(),
      Optional.of(2)
  ).flatMap(Optional::stream)
      .count();
  assertEquals(2, count);
}

Methods ifPresentOrElse() and stream() are also added to classes java.util.OptionalInt, java.util.OptionalDouble, and java.util.OptionalLong.

Summary

This chapter covered changes related to collections, Stream, and Optional in Java 9. The new factory methods for creating immutable collections can save a lot of code in Java programs. The new methods added to Arrays, Stream, and Optional are also very useful. In the next chapter, we’ll discuss the new Process API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.17.139