Stream Operations Overview

One of the greatest thing in Java 8 is new Stream API which essentially is a new fancy iterator over the collections or any other sequence of data. To work with Streams you need to compose a stream pipeline which consists of source, zero or more intermediate operations and a terminal operation.

Screen Shot 2016-03-26 at 09.39.36

The interfaces and classes that belongs to stream API can be found in java.util.stream package.

Source

According to the specifications a source might either an array,  a collection,  generator function or I/O streams.

Arrays

There are new methods in Arrays class that takes an array as parameter and returns stream:

int[] arr = new int[] {1, 2, 3, 4};
System.out.println(Arrays.stream(arr).sum()); 

// Output:
// 10
int[] arr = new int[] {1, 2, 3, 4};
System.out.println(IntStream.of(arr).sum()); 

// Output:
// 10

Collection

Collection interface was amended and new methods were added to work with stream API:

List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.stream().forEach(System.out::print); 

// Output:
// onetwo

Generator Function

In the below example I’m using iterator function which produces infinite sequence. Infinite stream can be converted to finite by limit function.

IntStream.iterate(0, n -> n + 1)
        .limit(10)
        .forEach(System.out::print); 

// Output:
// 0123456789

Another example with generator:

Stream.generate(Math::random)
         .limit(3)
         .forEach(System.out::println);

// Output:
// 0.8257196326524375
// 0.006161707932283322
// 0.551755540272633

I/O

There are many ways to apply new stream API with I/O, the below example is one of them. The example counts the number of lines in the file.

Path readmeFile = Paths.get("readme.txt");
Files.write(readmeFile, "1\n2\n3\n4\n5\n6\n7".getBytes());
System.out.println(Files.lines(readmeFile).count()); 

// Output:
// 7

Intermediate Operations

Intermediate operations can be easily distinguished because all these operations return stream. Another important point is that these operations are always lazy and won’t be executed until terminal operation is called. Laziness is effective way for optimizations and as a result all pipeline is execute in one pass. The intermediate operations work in conjunction with functional interfaces.

Filter

Takes: Predicate

As it’s name suggests it filters elements in the stream that matches the given predicate and returns stream.

IntStream.range(1, 10)
        .filter(value -> value % 2 == 0)
        .forEach(System.out::print); 

// Output:
// 2468

Map

Takes: Function

Transforms the stream by applying the given function to the elements of the stream. In the below example the stream of string arrays is transformed into stream of lengths.

Stream.of("Moscow", "London", "Johannesburg")
        .map(String::length)
        .forEach(
                length -> System.out.print(length + " ")
        ); 

// Output:
// 6 6 12

FlatMap

Takes: Function.

Lets say you have a stream of lists then you can use flatMap to transform it into one stream. As a result, the given stream is flattened.

List<String> list1 = Arrays.asList("1","2");
List<String> list2 = Arrays.asList("3","4");

Stream.of(list1,list2)
        .flatMap(list -> list.stream())
        .forEach(System.out::print); 

// Output:
// 1234

Limit

Takes: long

Limit can be used to make finite stream from infinite. In other words, it truncates the stream to be no longer than the given size.

IntStream.iterate(0, n -> n + 1)
        .limit(10)
        .forEach(System.out::print); 

// Output:
// 0123456789

Skip

Takes: long

Returns the infinite stream after skipping first n elements.

Stream.of(0, 1, 2, 3, 4, 5)
        .skip(3)
        .forEach(System.out::print); 

// Output:
// 345

Distinct

Takes: N/A

Removes duplicate values from the given stream using equals method .

Stream.of(0, 1, 2, 3, 4, 5, 5, 0, 1)
        .distinct()
        .forEach(System.out::print); 

// Output:
// 012345

Sorted

Takes: Comparator or nothing

Sorts the stream according to the natural ordering. Elements must be Comparable otherwise you can use another method that accepts Comparator.

Stream.of(0, 1, 2, 3, 4, 5, 5, 0, 1)
        .sorted()
        .forEach(
            s -> System.out.print(s + " ")
        ); 

// Output: 
// 0 0 1 1 2 3 4 5 5

Peek

Takes: Consumer

The method accepts consumer so it doesn’t return any results and it can be useful for debugging.

Stream.of(0, 1, -2, 3, -4)
        .filter(n -> n > 1)
        .peek(System.out::println)
        .map(n -> n * 100)
        .peek(System.out::println)
        .count(); 

// Output:
// 3
// 300

Terminal Operations

Once the terminal operation is completed the stream cannot be used again.

ForEach

Applies Consumer on each element in the stream.

Stream.of(1, 2, 3)
      .forEach(System.out::println);

// Output:
// 1
// 2
// 3

Min, Max

Return min and max elements in the stream wrapped into Optional container.

System.out.println(IntStream.of(1, 2, 3).min().getAsInt());
System.out.println(IntStream.of(1, 2, 3).max().getAsInt());

// Output:
// 1
// 3

Count

Returns the number of elements in the stream.

System.out.println(IntStream.of(1, 2, 3).count());

// Output:
// 3

AllMatch, AnyMatch, NoneMatch

The methods tests the stream using the given predicate.

System.out.println(IntStream.of(1, 2, 3).allMatch(n -> n > 0));
System.out.println(IntStream.of(1, 2, 3).anyMatch(n -> n < 0));
System.out.println(IntStream.of(1, 2, 3).noneMatch(n -> n > 0));

// Output:
// true
// false
// false

FindAny, FindFirst

Very simple methods that return first or any element of the stream correspondingly. In my example both methods returned the same result. But, findAny is free to return any other result. The result is wrapped into Optional container which helps to handle situations when stream for example is empty.

System.out.println(IntStream.of(1, 2, 3).findAny().getAsInt());
System.out.println(IntStream.of(1, 2, 3).findFirst().getAsInt());


// Output:
// 1
// 1

Reduce

If you need to reduce a stream into a single object this is what you need. There are several variations of the method. In the below example the first parameter is called identity and it’s basically a initial value that will be amended using the second parameter namely accumulator.

System.out.println(
        IntStream.of(1, 2, 3, 4, 5, 6)
                .reduce(1, (x, y) -> x * y)
);

System.out.println(
        Stream.of("J", "a", "v", "a")
                .reduce("", String::concat)
);

// Output:
// 720
// Java

Collect

You can use this method to perform various reduction operations on stream. I decided to make a separate post on this because there are a lot information that need to be described. So in this section I provided only one example in which the stream is converted into a Set.

Set<Integer> set = Stream.of(1, 2, 3, 4, 5, 5) .collect(Collectors.toCollection(TreeSet::new));
System.out.println(set);

// Output:
// [1, 2, 3, 4, 5]

Leave a Reply