Java Stream API operations that returns a new Stream are called intermediate operations. Most of the times, these operations are lazy in nature, so they start producing new stream elements and send it to the next operation.
Intermediate operations are never the final result
producing operations. Commonly used intermediate operations
are filter
and map
.
Java 8 Stream API operations that returns a result or produce a side effect. Once the terminal method is called on a stream, it consumes the stream and after that we can’t use stream.
Terminal operations are eager in
nature i.e they process all the elements in the stream before returning the
result. Commonly used terminal methods are forEach
, toArray
, min
, max
, findFirst
, anyMatch
, allMatch
etc
You can identify terminal methods from the return type, they will never return a Stream.
Java Stream Short Circuiting Operations
An intermediate operation is called short circuiting, if it may produce
finite stream for an infinite stream. For example limit()
and skip()
are two short circuiting intermediate operations.
A terminal operation is called short circuiting, if it may terminate in
finite time for infinite stream. For example anyMatch
, allMatch
, noneMatch
, findFirst
and findAny
are short circuiting terminal operations.
Java Stream Examples
I have covered almost all the important parts of the Java 8 Stream API. It’s exciting to use this new API features and let’s see it in action with some java stream examples.
Creating Java Streams
There are several ways through which we can create a java stream from array and collections. Let’s look into these with simple examples.
We can use Stream.of()
to create a stream from similar type of data. For example, we can create
Java Stream of integers from a group of int or Integer objects. :
Stream<Integer> stream = Stream.of(1,2,3,4);
We can use Stream.of()
with an array of Objects to return the stream. Note that it
doesn’t support autoboxing, so we can’t pass primitive type array.
Stream<Integer> stream = Stream.of(new Integer[]{1,2,3,4});//works fineStream<Integer> stream1 = Stream.of(new int[]{1,2,3,4});//Compile time error, Type mismatch: cannot convert from Stream<int[]> to Stream<Integer>
stream()
to create sequential stream and parallelStream()
to create parallel stream. :
List<Integer> myList = new ArrayList<>();for(int i=0; i<100; i++) myList.add(i);//sequential streamStream<Integer> sequentialStream = myList.stream();//parallel streamStream<Integer> parallelStream = myList.parallelStream();
Stream.generate()
and Stream.iterate()
methods to create Stream. :
Stream<String> stream1 = Stream.generate(() -> {return "abc";});Stream<String> stream2 = Stream.iterate("abc", (i) -> i);
Arrays.stream()
and String.chars()
methods.
LongStream is = Arrays.stream(new long[]{1,2,3,4});IntStream is2 = "abc".chars();
Converting Java Stream to Collection or Array
collect()
method to get List, Map or Set from stream.
Stream<Integer> intStream = Stream.of(1,2,3,4);List<Integer> intList = intStream.collect(Collectors.toList());System.out.println(intList); //prints [1, 2, 3, 4]intStream = Stream.of(1,2,3,4); //stream is closed, so we need to create it againMap<Integer,Integer> intMap = intStream.collect(Collectors.toMap(i -> i, i -> i+10));System.out.println(intMap); //prints {1=11, 2=12, 3=13, 4=14}
toArray()
method to create an array from the stream.
Stream<Integer> intStream = Stream.of(1,2,3,4);Integer[] intArray = intStream.toArray(Integer[]::new);System.out.println(Arrays.toString(intArray)); //prints [1, 2, 3, 4]
Java Stream Intermediate Operations
List<Integer> myList = new ArrayList<>();for(int i=0; i<100; i++) myList.add(i);Stream<Integer> sequentialStream = myList.stream();Stream<Integer> highNums = sequentialStream.filter(p -> p > 90); //filter numbers greater than 90System.out.print("High Nums greater than 90=");highNums.forEach(p -> System.out.print(p+" "));//prints "High Nums greater than 90=91 92 93 94 95 96 97 98 99 "
Stream<String> names = Stream.of("aBc", "d", "ef");System.out.println(names.map(s -> {return s.toUpperCase();}).collect(Collectors.toList()));//prints [ABC, D, EF]
Stream<String> names2 = Stream.of("aBc", "d", "ef", "123456");List<String> reverseSorted = names2.sorted(Comparator.reverseOrder()).collect(Collectors.toList());System.out.println(reverseSorted); // [ef, d, aBc, 123456]Stream<String> names3 = Stream.of("aBc", "d", "ef", "123456");List<String> naturalSorted = names3.sorted().collect(Collectors.toList());System.out.println(naturalSorted); //[123456, aBc, d, ef]
Stream flatMap() example: We can use flatMap() to create a stream from the stream of list. Let’s see a simple example to clear this doubt.:
Stream<List<String>> namesOriginalList = Stream.of(Arrays.asList("Pankaj"),Arrays.asList("David", "Lisa"),Arrays.asList("Amit"));//flat the stream from List<String> to String streamStream<String> flatStream = namesOriginalList.flatMap(strList -> strList.stream());flatStream.forEach(System.out::println);
Java Stream Terminal Operations
Stream<Integer> numbers = Stream.of(1,2,3,4,5);Optional<Integer> intOptional = numbers.reduce((i,j) -> {return i*j;});if(intOptional.isPresent()) System.out.println("Multiplication = "+intOptional.get()); //120
Stream<Integer> numbers1 = Stream.of(1,2,3,4,5);System.out.println("Number of elements in stream="+numbers1.count()); //5
Stream<Integer> numbers2 = Stream.of(1,2,3,4,5);numbers2.forEach(i -> System.out.print(i+",")); //1,2,3,4,5,
Stream<Integer> numbers3 = Stream.of(1,2,3,4,5);System.out.println("Stream contains 4? "+numbers3.anyMatch(i -> i==4));//Stream contains 4? trueStream<Integer> numbers4 = Stream.of(1,2,3,4,5);System.out.println("Stream contains all elements less than 10? "+numbers4.allMatch(i -> i<10));//Stream contains all elements less than 10? trueStream<Integer> numbers5 = Stream.of(1,2,3,4,5);System.out.println("Stream doesn't contain 10? "+numbers5.noneMatch(i -> i==10));//Stream doesn't contain 10? true
Stream<String> names4 = Stream.of("Pankaj","Amit","David", "Lisa");Optional<String> firstNameWithD = names4.filter(i -> i.startsWith("D")).findFirst();if(firstNameWithD.isPresent()){System.out.println("First Name starting with D="+firstNameWithD.get()); //David}
Java 8 Stream API Limitations
import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.stream.Stream;public class StatefulParallelStream {public static void main(String[] args) {List<Integer> ss = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);List<Integer> result = new ArrayList<Integer>();Stream<Integer> stream = ss.parallelStream();stream.map(s -> {synchronized (result) {if (result.size() < 10) {result.add(s);}}return s;}).forEach( e -> {});System.out.println(result);}}
- Once a Stream is consumed, it can’t be used later on. As you can see in above examples that every time I am creating a stream.
- There are a lot of methods in Stream API and the most confusing part is the overloaded methods. It makes the learning curve time taking.