Templates by BIGtheme NET

JAVA 8 Feature – Streams

In this article, I will show you how Streams java.util.stream can be useful with examples.

Immediate questions comes to mind,
Is it a new class in java collection framework? – Answer is NO
Is it a new Interface in java collection framework? – Answer is NO
Another question is, is it to be with java.io. package’s? – Answer is NO

What are streams?
A Stream is a free flowing sequence of elements.
They do not hold any storage as that responsibility lies with collections such as arrays,
lists and sets. The main purpose of Streams is to making the collections
framework to better.

Streams differ from traditional collections in several ways.
1) No storage: A stream is not a data structure that stores elements;
instead, it conveys elements from a source such as a data structure, an array, a generator function,
or an I/O channel, through a pipeline of computational operations.
2) Functional in nature: An operation on a stream produces a result, but does
not modify its source.
For example, filtering a Stream obtained from a collection produces a new Stream without the
filtered elements, rather than removing elements from the source collection.
3) Laziness-seeking: Many stream operations, such as filtering, mapping, or
duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example,
“find the first String with three consecutive vowels” need not examine all the input strings.
Stream operations are divided into intermediate (Stream-producing) operations and terminal
(value- or side-effect-producing) operations. Intermediate operations are always lazy.
4) Possibly unbounded: While collections have a finite size, streams need not.
Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams
to complete in finite time.
5) Consumable: The elements of a stream are only visited once during the life of
a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.

How to create Streams :
1) we can create streams from any of collection object.

// How to create stream from ArrayList object
final List<Bank> banks = new ArrayList<>();
final Stream<Bank> bankStream = banks.stream();

// How to create stream for Map keys and values
final Map<String,String> map = new HashMap<String, String>();
final Stream<String> keySetStream = map.keySet().stream();
final Stream<String> valueSetStream = map.values().stream();

// How to create stream from HashSet object
Set<String> set = new HashSet<String>();
final Stream<String> setStream = set.stream();

2) we can create from set of object or objects.

Bank sbi = new Bank("SBI", "SBI_CODE", 11);
Bank icici = new Bank("ICICI", "ICICI_CODE", 22);
Bank hdfc = new Bank("HDFC", "HDFC_CODE", 33);
// Create a stream object for a single Bank object
final Stream<Bank> sbiBanksStream = Stream.of(sbi);
// Create a stream object for three Bank objects
final Stream<Bank> allBanksStream = Stream.of(sbi, icici, hdfc);

3) we can use substream() method on stream object to create sub stream from actual stream.

Stream<Bank> bankSubStream = stream.substream();

4) There are some intermediate operations like map, filter and limit. They can produce streams as well.

//How to get only SBI banks stream from allBankStream
final Stream<Bank> sbibanksStream = bankStream.filter(b -> b
				.getBankName().equals("SBI"));
// How to apply limit operation on stream
final Stream<Bank> allBanksSubStream = allBanksStream.limit(2);
// How to use map() method on streams
int ifscCodesum = Stream.of(sbi, icici, hdfc)
                        .mapToInt(b -> b.getIfscCode()).sum();

CreateStreams.java

package com.venkatajavasource.java8.streams;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

public class StreamsOperations {

	public static void main(String[] args) {
		System.out.println("Hello JAVA8 Streams");
		Bank sbi = new Bank("SBI", "SBI_CODE", 11);
		Bank icici = new Bank("ICICI", "ICICI_CODE", 22);
		Bank hdfc = new Bank("HDFC", "HDFC_CODE", 33);

		// How to create streams from a collection - List
		List<Bank> banks = new ArrayList();
		banks.add(sbi);
		banks.add(icici);
		banks.add(hdfc);
		final Stream<Bank> bankStream = banks.stream();
		// How to create streams from a collection - Map
		final Map<String, String> map = new HashMap<String, String>();
		final Stream<String> keySetStream = map.keySet().stream();
		final Stream<String> valueSetStream = map.values().stream();
		// How to create streams from a collection - Set
		Set<String> set = new HashSet<String>();
		final Stream<String> setStream = set.stream();

		// How to create streams from a set of objects
		final Stream<Bank> sbiBanksStream = Stream.of(sbi);
		final Stream<Bank> allBanksStream = Stream.of(sbi, icici, hdfc);

		// How to create streams from intermediate operator like filter, 
		// map and limit
		System.out.println("n************STREAMS filter() METHOD***********");
		// filter operation on streams, get only SBI banks
		final Stream<Bank> sbibanksStream = bankStream.filter(b -> b
				.getBankName().equals("SBI"));
		printlBankDetails(sbibanksStream);

		System.out.println("n************STREAMS limit() METHOD***********");
		// Only first two objects details will be printed, as we provide 
		// value 2 in limit method.
		final Stream<Bank> allBanksSubStream = allBanksStream.limit(2);
		printlBankDetails(allBanksSubStream);

		System.out.println("n************STREAMS map() METHOD***********");
		int ifscCodesum = Stream.of(sbi, icici, hdfc)
				.mapToInt(b -> b.getIfscCode()).sum();
		System.out.println("ifscCodesum: " + ifscCodesum);
	}

	private static void printlBankDetails(final Stream<Bank> inBankStream) {
		inBankStream.forEach(each -> {
			System.out.println("Bank Name : " + each.getBankName());
			System.out.println("Bank IFSI Code : " + each.getIfscCode());
		});
	}

	public static class Bank {
		private String bankName;
		private String bankCode;
		private int ifscCode;

		public Bank(final String inBankName, final String inBankCode,
				final int inIfscCode) {
			bankName = inBankName;
			bankCode = inBankCode;
			ifscCode = inIfscCode;
		}

		public String getBankName() {
			return bankName;
		}

		public int getIfscCode() {
			return ifscCode;
		}
	}
}

Select “Run As” -> “Java Application”
Out Put :

Hello JAVA8 Streams

************STREAMS filter() METHOD***********
Bank Name : SBI
Bank IFSI Code : 11

************STREAMS limit() METHOD***********
Bank Name : SBI
Bank IFSI Code : 11
Bank Name : ICICI
Bank IFSI Code : 22

************STREAMS map() METHOD***********
ifscCodesum: 66

Stream operations:
Stream operations are classified as intermediate and terminal operators.

1) Intermediate operations:
Intermediate operators apply logic so the incoming stream produces yet another stream.
A stream can have countless intermediate operators attached to it, and includes such
operations as filters, sort and maps.
Intermediate operators set up the pipeline but do not begin to execute until a terminal operator is associated with the stream, which can be found at the end of the call stack.
Predicates and Filters :
Predicates are simple boolean functions that can take zero or more parameters. In Java 8, predicates are used to filter stream objects.The stream method filter() takes as its parameter a predicate function which is applied to each steam object input to the filter call. The output of a filter call is a stream of objects that satisfy (or return true) in the parameter (Lambda) predicate.

In this example below, I define a named Predicate instance that operates on Person objects. The predicate takes one Integer parameter, age. A person object returns true when the person’s age is greater than the parameter age.

private static Predicate isAgeGreaterThan(Integer age) {
  return personObj -> personObj.getAge() > age;
}

Next is to defined Predicate is used as a parameter to a filter function applied to a stream of Person objects. Note that Person objects that satisfy or pass the filter are collected into a map dividing the Persons into FEMALE and MALE buckets.

package com.venkatajavasource.java8.streams;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class StreamsOperationsPredicateAndFilter {

	public static void main(String[] args) {
		final List<Person> personList = Arrays.asList(
				new Person(31, SEX.MALE), new Person(28, SEX.MALE), 
				new Person(32, SEX.MALE), new Person(38, SEX.MALE), 
				new Person(31, SEX.FEMALE), new Person(28, SEX.FEMALE), 
				new Person(32, SEX.FEMALE),new Person(18, SEX.FEMALE));
		
		testPredicate(personList);
	}

	private static void printlBankDetails(final Map<SEX, List<Person>> inPersons) {
		inPersons.keySet().stream().forEach(eachKey -> {
			System.out.println("Key is : " + eachKey );
			inPersons.get(eachKey).stream().forEach( 
					eachValue ->  { System.out.println("Person Age is : " + eachValue.getAge()); 
									System.out.println("Person Gender is : " + eachValue.getGender()); });
			System.out.println("*************************************");
		});
	}

	private static Predicate<Person> isAgeGreaterThan(Integer age) {
		return p -> p.getAge() > age;
	}

	public static void testPredicate(final List<Person> inPersonList) {
		Map<SEX, List<Person>> persons = inPersonList.stream()
				.filter(isAgeGreaterThan(30)) // filter collection
				.collect(Collectors.groupingBy(Person::getGender));
		//System.out.println(persons);
		printlBankDetails(persons);
	}

	public static class Person {
		private int age;
		private SEX gender;

		public Person(final int inAge, final SEX inGender) {
			age = inAge;
			gender = inGender;
		}

		public int getAge() {
			return age;
		}

		public SEX getGender() {
			return gender;
		}
	}

	public static enum SEX {
		FEMALE, MALE;
	}
}

Select “Run As” -> “Java Application”
Out Put :

Key is : FEMALE
Person Age is : 31
Person Gender is : FEMALE
Person Age is : 32
Person Gender is : FEMALE
*************************************
Key is : MALE
Person Age is : 31
Person Gender is : MALE
Person Age is : 32
Person Gender is : MALE
Person Age is : 38
Person Gender is : MALE
*************************************

Function and map :
Functions are Java methods that have one parameter and a return type.
Like Predicates, they can be named, which is useful for re-usability, or they can be
anonymously defined as parameters to methods that have a Function as parameter.
Functions are generically specified with two type parameters (type of input parameter,
type of return value).

 Function<R, T> name = (param) -> {body} 

Functions are used as parameters to the stream map method. The map method applies the
function to each object in the stream.
The function must take the stream object type as its input type. The map function returns
a stream of objects whose type is the function’s return type.
The map function transforms the input stream into an output stream of objects.
The function does the transformation on each stream object.

private static Function increment = (x) -> x + 1;
private static Function testOddness = (p) -> p % 2==0?"EVEN":"ODD";

Next, the function increment is used to transform a List of Integers into another list of
Integers (each Integer object is incremented). The testOddness function is used as
a classification function for the groupingBy function.
This is a reduce step that organizing the stream of Integer objects from the stream into
buckets labeled “ODD” and “EVEN”, which are the return values from the testOddness function.

package com.venkatajavasource.java8.streams;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class StreamsOperationsFunctionAndMap {

	public static void main(String[] args) {
		final List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9,
				10);
		testFunction(intList);
	}

	public static void testFunction(final List<Integer> inIntList) {
		Map<String, List<Integer>> sorted = inIntList.stream()
				.map(p -> inc.apply(p))
				.collect(Collectors.groupingBy(p -> testOddness.apply(p)));
		System.out.println(sorted);
	}

	private static Function<Integer, Integer> inc = (x) -> x + 1;

	private static Function<Integer, String> testOddness = (p) -> p % 2 == 0 ? "EVEN"
			: "ODD";
}

Select “Run As” -> “Java Application”
Out Put :

{EVEN=[2, 4, 6, 8, 10], ODD=[3, 5, 7, 9, 11]}

Functions can be anonymously passed to map or reduce functions. Here’s the same code using
anonymous lambda versions of the functions.

public static void testAnonymousLambdaFunction() {
 Map<String, List<Integer>> sorted = intList.stream()
 .map(p -> p + 1)
 .collect(Collectors.groupingBy(p -> p % 2 == 0 ? "EVEN" : "ODD"));
 System.out.println(sorted);
}

2) Terminal operations:
The final operation to consume the stream is a terminal operation. Examples of terminal
operators include forEach, reduce and collect.

forEach :
Instead of for loop and iterable, we can use forEach.
Equivalent of for(String s:strList) { System.out.println(s); }
is strList.stream().forEach(System.out::println);

inBankStream.forEach(each -> {
			System.out.println("Bank Name : " + each.getBankName());
			System.out.println("Bank IFSI Code : " + each.getIfscCode());
		});

reduce :
Performing the reduce method on a stream is useful to achieve a single accumulated figure.
For Example, get the total of given list of integers.

final List<Integer> intList = 
					Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println("Total is: " + 
	intList.stream().map(p -> p + 1).reduce((a,b) -> (a+b)).get());

collect :
We can also create additional data storage from the stream by using the collect method.
For Example, to collect “ODD” and “EVEN” numbers into two different groups.

public static void testFunction( final List<Integer> inIntList ) {
		 Map<String, List<Integer>> sorted = inIntList.stream()
		 .map(p -> p + 1)		 
		 .collect(Collectors.groupingBy(p -> p % 2 == 0 ? "EVEN" : "ODD"));
		 System.out.println(sorted);
		}

Download the source code here:
JAVA8Features.7z

*** Venkat – Happy leaning ****