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.
The interfaces and classes that belongs to stream API can be found in java.util.stream package.
Contents
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]