Stream intermediate operations

This example will show various code snippets to demonstrate stream intermediate operations. Intermediate operations return another Stream which allows you to call multiple operations in a form of a query. Intermediate operations do not get executed until a terminal operation is invoked as there is a possibility they could be processed together when a terminal operation is executed.

filter

Stream.filter operation will return a new stream that contains elements that match its predicate. Below we will use a lambda expression to create a java.util.function.Predicate that will check if the integer is less than three. The terminal Stream.count operation will return the number of elements in the stream.

@Test
public void intermediate_filter() {

    long elementsLessThanThree = Stream.of(1, 2, 3, 4)
            .filter(p -> p.intValue() < 3).count();

    assertEquals(2, elementsLessThanThree);
}

map

Stream.map will transform the elements elements in a stream using the provided java.util.function.Function. A function is a method that accepts an argument and produces a result. They are commonly used for transforming collections and can be seen in the transforming a list to a map example.

In the snippet below we will create a function using a lambda expression that will replace any null strings in the stream with [unknown].

@Test
public void intermediate_map() {

    List<String> strings = Stream.of("one", null, "three").map(s -> {
        if (s == null)
            return "[unknown]";
        else
            return s;
    }).collect(Collectors.toList());

    assertThat(strings, contains("one", "[unknown]", "three"));

}

flatmap

Stream.flatmap will transform each element into zero or more elements by a way of another stream. To demonstrate, we pulled code snippet from how to count unique words in a file example. Using java 7 file api, we will read all lines from a file as a Stream. Then calling Stream.flatmap we will break the line into words elements. If we had a line made up of "the horse was walking down the street", this line would be broken into ["the", "horse", "was", "walking", "down", "the", "street"]. Calling the the Stream.distinct method will find all unique occurrences of words.

@Test
public void count_distinct_words_java8() throws IOException {

    File file = new File(sourceFileURI);

    long uniqueWords = java.nio.file.Files
            .lines(Paths.get(file.toURI()), Charset.defaultCharset())
            .flatMap(line -> Arrays.stream(line.split(" ."))).distinct()
            .count();

    assertEquals(80, uniqueWords);
}

peek

The Stream.peek is extremely useful during debugging. It allows you to peek into the stream before an action is encountered. In this snippet we will filter any strings with a size of great than four then call the peek at the stream before the map is called. The peek operation will print out the elements of 'Badgers', 'finals' and 'four'.

@Test
public void intermediate_peek() {

    List<String> strings = Stream.of("Badgers", "finals", "four")
            .filter(s -> s.length() >= 4).peek(s -> System.out.println(s))
            .map(s -> s.toUpperCase()).collect(Collectors.toList());
    
    assertThat(strings, contains("BADGERS", "FINALS", "FOUR"));
}

distinct

Stream.distinct will find unique elements in a stream according to their .equals behavior. A use case to use distinct is when you want to find the distinct number of words in a file.

@Test
public void intermediate_distinct() {
    
    List<Integer> distinctIntegers = IntStream.of(5, 6, 6, 6, 3, 2, 2)
            .distinct()
            .boxed()
            .collect(Collectors.toList());

    assertEquals(4, distinctIntegers.size());
    assertThat(distinctIntegers, contains(
            5, 6, 3, 2));       
    
}

sorted

The Stream.sorted method will return a stream sorted according to natural order. Below, we will create a stream of ints then calling the sort which will return the numbers in ascending order.

@Test
public void intermediate_sorted() {

    List<Integer> sortedNumbers = Stream.of(5, 3, 1, 3, 6).sorted()
            .collect(Collectors.toList());

    assertThat(sortedNumbers, contains(1, 3, 3, 5, 6));
}

limit

Stream.limit is a useful technique to limit the number or truncate elements to be processed in the stream. Similar to limit elements in a list, we can perform the same behavior within a stream as shown below.

@Test
public void intermediate_limit() {

    List<String> vals = Stream.of("limit", "by", "two").limit(2)
            .collect(Collectors.toList());

    assertThat(vals, contains("limit", "by"));
}