Understanding Java 8 Streams using examples | JavaInUse



Understanding Java 8 Streams using examples

In this post we will understand the Java 8 Streams using simple examples.
The section is divided into following sections-
  • What are Java Streams.
  • Streams and Collections.
  • Generate Streams from other DataStructures.
  • Convert Streams to Other DataStructures.
  • Various Stream operations.
    • Intermediate Operations.
    • Terminal Operations.

What are Java Streams?

Consider you have a sequence of elements 1,2,3,4,5,6,7,8,9,10 and you have to find the maximum of these numbers.
In traditional Java prior to java 8 we will have to write a for loop and then write logic to get the largest of these numbers.
Whereas in SQL similar work can be done by writing a simple query
SELECT max(numId) from employee
Here we just declare what we want and not specify the how to do it. From Java 8 we can achieve something similar using Streams. Java 8 has introduced the java.util.stream which allows users to process sequence of elements. So what does one mean by processing of elements? By processing means performing operations like filter, map, limit, reduce, find, match etc.

Streams and Collections-

We make use of Streams or Collections when we have to work on sequence of elements. Collections are in-memory data structures which holds elements. These elements are to be computed before they actually are added to the Collections.
Conversely Streams are fixed data structures where elements are computed only when required . So it can be viewed that collections have elements computed eagerly, while Streams have elements computed lazily.

Generate Streams from other DataStructures

Different ways to generate Stream-
  • Generate Streams from Arrays using .stream or Stream.of.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.stream.Stream;
    
    public class MainClass {
    
    	public static void main(String[] args) {
    
    		String[] testArray = { "a", "b", "c", "d", "e" };
    
    		// Get Stream using the Arrays.stream
    		Stream<String> testStream1 = Arrays.stream(testArray);
    		testStream1.forEach(x -> System.out.println(x));
    
    		// Get Stream using the Stream.of
    		Stream<String> testStream2 = Stream.of(testArray);
    		testStream2.forEach(x -> System.out.println(x));
    	}
    
    }
     

  • Generate Streams from Collections.
    package com.javainuse;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String[] args) {
    
    		List<String> items = new ArrayList<>();
    		items.add("test1");
    		items.add("test2");
    		items.add("test3");
    		items.add("test4");
    		items.add("test5");
    
    		items.stream().forEach(item -> System.out.println(item));
    
    	}
    }
    	  

  • Generate Streams using Stream.generate()
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.stream.Stream;
    
    public class MainClass {
    
    	public static void main(String[] args) {
    		Stream<String> stream = Stream.generate(() -> "hello").limit(10);
    		String[] testStrArr = stream.toArray(String[]::new);
    		System.out.println(Arrays.toString(testStrArr));
    	}
    }
    	  

  • Generate Streams using Stream.iterate().
    package com.javainuse;
    
    import java.util.stream.Stream;
    
    public class MainClass {
    
    	public static void main(String[] args) {
    		Stream<Long> iterateNumbers = Stream.iterate(1L, n -> n + 1).limit(10);
    		iterateNumbers.forEach(System.out::println);
    	}
    }
    

  • Generate Streams from APIs like Regex.
    	  package com.javainuse;
    
    import java.util.regex.Pattern;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		String str = "THIS,IS,JAVA!!!!!";
    		Pattern.compile(",").splitAsStream(str).forEach(System.out::println);
    	}
    }
    	  

Convert Streams to Other DataStructures

Different ways to get Collections from Streams-
  • Get Collections using stream.collect(Collectors.toList()).
    package com.javainuse;
    
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		Stream<String> stream = Stream.of("java", "in", "use");
    		List<String> testNames = stream.collect(Collectors.toList());
    
    		System.out.println(testNames);
    	}
    }
    	  

  • Get arrays using stream.toArray(EntryType[]::new)
    	  package com.javainuse;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Stream;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<Integer> list = new ArrayList<>();
    		list.add(1);
    		list.add(2);
    		list.add(3);
    		list.add(4);
    		Stream<Integer> intStream = list.stream();
    		Integer[] evenArr = intStream.filter(i -> i % 2 == 0).toArray(
    				Integer[]::new);
    		System.out.print(evenArr[0]);
    	}
    }
    
    	  

Different Stream operations

Stream operations can be divided in two parts-
  • Intermediate operations- Intermediate operations which return another Stream which allows operations to be connected in a form of a query
  • Terminal operations- Terminal operations produces a non-stream, result such as primitive value, a collection or no value at all.

Intermediate operations


  • filter() operation helps eliminate elements based on certain criteria.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<String> data = Arrays.asList("java", "not", "in", "use");
    
    		data.stream().filter(line -> !"not".equals(line))
    				.forEach(p -> System.out.println(p));
    
    	}
    }
    

  • map() operation helps map elements to the corresponding results.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<String> data = Arrays.asList("java", "not", "in", "use");
    
    		data.stream().filter((s) -> !s.startsWith("n"))
    				.map(String::toUpperCase).forEach(System.out::println);
    
    	}
    }
    

  • sorted() operation helps sort elements based on certain criteria.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<String> data = Arrays.asList("java", "not", "in", "use");
    
    		data.stream().sorted().filter((s) -> !s.startsWith("n"))
    				.map(String::toUpperCase).forEach(System.out::println);
    
    	}
    }
    
    

Terminal Operations


  • foreach operations helps iterate the elements of the Stream.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<String> data = Arrays.asList("java", "not", "in", "use");
    
    		data.stream().sorted().filter((s) -> !s.startsWith("n"))
    				.map(String::toUpperCase).forEach(System.out::println);
    
    	}
    }
    

  • collect() operation helps to collect the stream result in a collection like list.
     package com.javainuse;
    
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		Stream<String> stream = Stream.of("java", "in", "use");
    		List<String> testNames = stream.collect(Collectors.toList());
    
    		System.out.println(testNames);
    	}
    }
    

  • match() operation returns a boolean depending upon the condition applied on stream data.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<String> data = Arrays.asList("java", "not", "in", "use");
    
    		boolean result = data.stream().anyMatch((s) -> s.startsWith("j"));
    		System.out.println(result);
    	}
    }
    
    

  • count() operation return the aggregate count for stream data.
    package com.javainuse;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    		List<String> data = Arrays.asList("java", "not", "in", "use");
    
    		long result = data.stream().filter((s) -> s.startsWith("j")).count();
    
    		System.out.println(result);
    	}
    }
    

  • reduce() operation combines the stream elements into one using a BinaryOperator.
    package com.javainuse;
    
    import java.util.stream.IntStream;
    
    public class MainClass {
    
    	public static void main(String args[]) {
    
    		int result = IntStream.of(1, 2, 3, 4).reduce(0, (a, b) -> a + b);
    		System.out.println(result);
    	}
    }
    

See Also

Java - PermGen space vs MetaSpace Understand Java 8 Method References using Simple Example Java 8 Lambda Expression- Hello World Example Java 8-Internal vs. External Iteration. Understanding Java 8 Optional using examples.