Core Java

Java 8 Stream Collectors groupingBy Example

1. Introduction

SQL GROUP BY is a very useful aggregation function. It groups database records on certain criteria. Java 8 Stream API enables developers to process collections of data in a declarative way. Java 8 Collectors class provides a static groupingBy method: to group objects by some property and store the results in a Map instance. It has three signatures, which are shown below.

ReturnMethod
static <T,K> Collector<T,?,Map<K,List<T>>>groupingBy(Function<? super T,? extends K> classifier)
static <T,K,A,D> Collector<T,?,Map<K,D>>groupingBy(Function<? super T,? extends K> classifier,Collector<? super T,A,D> downstream)
static <T,K,D,A,M extends Map<K,D>>
Collector<T,?,M>
groupingBy(Function<? super T,? extends K> classifier,Supplier<M> mapFactory, Collector<? super T,A,D> downstream)

 

 
In this example, I will demonstrate how to use Stream and Collectors.groupingBy to group objects based on a simple data type, enumeration, and complex object type.

2. Technologies used

The example code in this article was built and run using:

  • Java 1.8.101
  • Maven 3.3.9
  • Eclipse Oxygen
  • JUnit 4.12

3. Maven Project

3.1 Dependency

Add JUnit to the pom.xml.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>zheng.jcg.demo</groupId>
	<artifactId>java8-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.3</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

3.2 Domain Objects

I will define several classes to demonstrate the usage of groupingBy. I will group a list of fruits based on its type, likes, quantity, and total cost.

3.2.1 Fruit Type

I will define an enumeration class for the fruit type.

FruitType.java

package com.zheng.demo.domain;

public enum FruitType {
	APPLE, BANANA, PAPAYA, ORGNGE, WATERMELON;
}

3.2.2 Fruit

I will define a fruit class which contains unique ID, name, type, likes, quantity, and unit price.

Fruit.java

package com.zheng.demo.domain;

import java.math.BigDecimal;

public class Fruit {
	public Fruit(int id, String name, FruitType type, int quantity, BigDecimal price, int likes) {
		super();
		this.id = id;
		this.name = name;
		this.type = type;
		this.quantity = quantity;
		this.price = price;
		this.likes = likes;
	}
	private int id;
	private String name;
	private FruitType type;
	private int quantity;
	private BigDecimal price;
	private int likes;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	public BigDecimal getPrice() {
		return price;
	}

	public void setPrice(BigDecimal price) {
		this.price = price;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getLikes() {
		return likes;
	}

	public void setLikes(int likes) {
		this.likes = likes;
	}
	
	public Double getTotal() {
		return this.quantity * this.price.doubleValue();
	}
	
	public String getTypeLikes() {
		return this.type.name() + this.likes;
	}

	public FruitType getType() {
		return type;
	}

	public void setType(FruitType type) {
		this.type = type;
	}

	@Override
	public String toString() {
		return "Product [id=" + id + ", name=" + name + ", type=" + type + ", quantity=" + quantity + ", price=" + price
				+ ", likes=" + likes + "]";
	}

}

3.2.3 Type Quantity

I will define a class which contains a fruit type and quantity.

TypeQuantity.java

package com.zheng.demo.domain;

public class TypeQuantity {

	private FruitType type;
	private int quantity;

	public TypeQuantity() {
		super();
	}

	public TypeQuantity(FruitType type, int quantity) {
		super();
		this.type = type;
		this.quantity = quantity;
	}

	public FruitType getType() {
		return type;
	}

	public void setType(FruitType type) {
		this.type = type;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + quantity;
		result = prime * result + ((type == null) ? 0 : type.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		TypeQuantity other = (TypeQuantity) obj;
		if (quantity != other.quantity)
			return false;
		if (type != other.type)
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "TypeQuantity [type=" + type + ", quantity=" + quantity + "]";
	}

}

3.3 Prior Java 8

I will demonstrate how to group a list of fruits based on the type without using Java 8’s Stream and Collector.

GroupBy_PriorJDK8.java

package com.zheng.demo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.zheng.demo.domain.Fruit;
import com.zheng.demo.domain.FruitType;

public class GroupBy_PriorJDK8 {
	
	private List<Fruit> fruits;
	
	public GroupBy_PriorJDK8(List<Fruit> fruits) {
		super();
		this.fruits = fruits;
	}
	
	public Map<FruitType, List<Fruit>> groupByType(){
		Map<FruitType, List<Fruit>> items = new HashMap<>();
		for(Fruit fruit: fruits) {
			if( !items.containsKey(fruit.getType())) {
				items.put(fruit.getType(), new ArrayList<>());
			}
			
			items.get(fruit.getType()).add(fruit);
		}
		return items;
	}

}

3.4 Java 8 GroupingBy for a List of String

I will demonstrate how to group a list of Strings by creating a WordCounts class which includes:

  • getWordCounts – group a given sentence by its word’s counts
  • getWordLength – group a given sentence by its word’s length
  • getWordLength_List – group a given sentence by its words’ length, return it as a List
  • getWordLength_Set – group a given sentence by its words’ length, return it as a Set
  • getwordLength_String – group a given sentence by its words’ length and return it as a String

WordCounts.java

package com.zheng.demo;

import java.util.Arrays;
import java.util.Collection;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;

public class WordCounts {
	
	public WordCounts(String sentence) {
		super();
		setSentence(sentence);
	}

	private String sentence;
	private String[] words;
	
	public Map<String, Long> getWordCounts(){		
		return Arrays.stream(words)
				.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
	}
	
	public Map<Integer, List<String>> getWordLength(){	
		return Arrays.stream(words)
				.collect(Collectors.groupingBy(String::length));
	}
	
	public Map<Integer, Collection<String>> getWordLength_Set(){	
		return Arrays.stream(words)
				.collect(Collectors.groupingBy(String::length, Collectors.toCollection(TreeSet::new)));
	}
	
	public Map<Integer, List<String>> getWordLength_List(){	
		return Arrays.stream(words)
				.collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.toList()));
	}
	
	public Map<Integer, String> getWordLength_String(){	
		return Arrays.stream(words)
				.collect(Collectors.groupingBy(String::length, Collectors.joining("|","[","]")));
	}
	
	
	public Map<Integer, IntSummaryStatistics> getWordLength_summarizingInt(){	
		return Arrays.stream(words)
				.collect(Collectors.groupingBy(String::length, Collectors.summarizingInt(String::hashCode)));
	}

	protected String getSentence() {
		return sentence;
	}

	protected void setSentence(String sentence) {		
		this.sentence = sentence.replaceAll("[;,.]","");
		words = this.sentence.split(" ");
	}

}

3.5 Java 8 GroupingBy for a List of Objects

I will demonstrate how to group a list of objects by creating a SalesProducts which contains:

  • groupByLikes – group by the likes
  • groupByLikesWithCounts – group by the likes, return its counts
  • groupByQuantity – group by the quantity
  • groupByType – group by the type
  • groupByTypeAndLike – group by both type and likes using a method
  • groupB yTypeAndLike_2 – group by both type and likes using two groupingBy methods
  • groupByTypeConcurrently – group by the type concurrently
  • groupByTypeQuantity – group by the type and quantity with a TypeQuantity class
  • groupByTypeSet – group by the type, return it as a set
  • groupByTypeWithAverageSum – group by the type, return the average of the total cost
  • groupByTypeWithQuantitySum – group by the type, return the total quantity
  • groupByTypeWithTotalSum – group by the type, return the total cost

SalesProducts.java

package com.zheng.demo;

import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.zheng.demo.domain.Fruit;
import com.zheng.demo.domain.FruitType;
import com.zheng.demo.domain.TypeQuantity;

public class SaleProducts {
	private List<Fruit> fruits;

	public SaleProducts(List<Fruit> fruits) {
		super();
		this.fruits = fruits;
	}

	//simplest grouping by an enum with an integer, toList() is the default
	public Map<Integer, List<Fruit>> groupByLikes() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getLikes));
	}

	//grouping by a likes and map it to counts
	public Map<Integer, Long> groupByLikesWithCounts() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getLikes, Collectors.counting()));
	}
	
	//simplest grouping by an integer
	public Map<Integer, List<Fruit>> groupByQuantity() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getQuantity));
	}
	
	//simplest grouping by an enum with a string
	public Map<FruitType, List<Fruit>> groupByType() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getType));
	}
	
	// simplest grouping by an enum with lambda, toList() is the default
	public Map<FruitType, List<Fruit>> groupByType_lambda() {
		return fruits.stream().collect(Collectors.groupingBy(f -> f.getType()));
	}

	//group by more than two classification with a method
	public Map<String, List<Fruit>> groupByTypeAndLike() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getTypeLikes));
	}
	
	//group by more than two classification with two groupingBy
	public Map<FruitType, Map<Integer, List<Fruit>>> groupByTypeAndLike_2() {
		return fruits.stream()
				.collect(Collectors.groupingBy(Fruit::getType, Collectors.groupingBy(Fruit::getLikes)));
	}

	//concurrent grouping by with enum
	public Map<FruitType, List<Fruit>> groupByTypeConcrrently() {
		return fruits.parallelStream().collect(Collectors.groupingByConcurrent(Fruit::getType));
	}

	//group by more than two classification with a complex type
	public Map<TypeQuantity, List<Fruit>> groupByTypeQuantity(){
		return fruits.stream().collect(Collectors.groupingBy( fruit -> new TypeQuantity(fruit.getType(), fruit.getQuantity())));
	}

	//grouping by an enum with a string, then change the results to set
	public Map<FruitType, Set<Fruit>> groupByTypeToSet() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getType, Collectors.toSet()));
	}
	
	public Map<FruitType, Double> groupByTypeWithAverageSum() {
		return fruits.stream()
				.collect(Collectors.groupingBy(Fruit::getType, Collectors.averagingDouble(Fruit::getTotal)));
	}

	//grouping by a type and map it to counts
	public Map<FruitType, Long> groupByTypeWithCounts() {
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getType, Collectors.counting()));
	}

	//groupingBy with aggregation functions
	public Map<FruitType, Integer> groupByTypeWithQuantitySum() {
		return fruits.stream()
				.collect(Collectors.groupingBy(Fruit::getType, Collectors.summingInt(Fruit::getQuantity)));
	}
	
	public Map<FruitType, Double> groupByTypeWithTotalSum() {
		return fruits.stream()
				.collect(Collectors.groupingBy(Fruit::getType, Collectors.summingDouble(Fruit::getTotal)));
	}
	
	public Map<FruitType, DoubleSummaryStatistics> groupByType_TotalSummary() {		
		return fruits.stream().collect(Collectors.groupingBy(Fruit::getType, Collectors.summarizingDouble(Fruit::getTotal)));
	}

}

4. JUnit Tests

4.1 GroupBy_PriorJDK8Test

I will create JUnit test cases to test GroupBy_PriorJDK8.

GroupBy_PriorJDK8Test.java

package com.zheng.demo;

import static org.junit.Assert.assertEquals;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;

import com.zheng.demo.domain.Fruit;
import com.zheng.demo.domain.FruitType;

public class GroupBy_PriorJDK8Test {
	private GroupBy_PriorJDK8 salesItems;

	@Before
	public void setup() {
		salesItems = new GroupBy_PriorJDK8(Arrays.asList(
				new Fruit(1, "Fuji", FruitType.APPLE, 10, new BigDecimal("10"), 3),
				new Fruit(2, "Apple Banana", FruitType.BANANA, 20, new BigDecimal("20"), 2),
				new Fruit(3, "Mandarin Orange", FruitType.ORGNGE, 10, new BigDecimal("30"), 4),
				new Fruit(4, "Seedless Watermelon", FruitType.WATERMELON, 10, new BigDecimal("30"), 2),
				new Fruit(5, "Papaya", FruitType.PAPAYA, 20, new BigDecimal("10"), 1),
				new Fruit(6, "Gala", FruitType.APPLE, 10, new BigDecimal("8"), 5),
				new Fruit(7, "Cuban Red Banana", FruitType.BANANA, 10, new BigDecimal("15"), 2),
				new Fruit(8, "Golden Delicious", FruitType.APPLE, 10, new BigDecimal("6"), 5)));
	}

	@Test
	public void groupByType_PriorJava8() {
		Map<FruitType, List<Fruit>> groupByItems = salesItems.groupByType();
		System.out.println("groupByType_priorJava8: " + groupByItems);
		assertEquals(1, groupByItems.get(FruitType.PAPAYA).size());
		assertEquals(2, groupByItems.get(FruitType.BANANA).size());
		assertEquals(3, groupByItems.get(FruitType.APPLE).size());
	}

}

4.2 WordCountsTest

I will create JUnit test cases to test the WordCounts.

WordCountsTest.java

package com.zheng.demo;

import static org.junit.Assert.assertEquals;

import java.util.Collection;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;

public class WordCountsTest {

	private WordCounts wordCounts;
	
	@Before
	public void setup() {
		wordCounts = new WordCounts("John likes beef, Mary likes beef, Tom likes beef; but Allen and Jerry hate beef.");
	}

	
	@Test
	public void getWordCounts() {	
		Map<String, Long> wc = wordCounts.getWordCounts();
		System.out.println(wc);
		
		assertEquals(1, wc.get("John").longValue());
		assertEquals(3, wc.get("likes").longValue());
		assertEquals(4, wc.get("beef").longValue());
		assertEquals(1, wc.get("Jerry").longValue());
	}
	
	@Test
	public void getWordLength() {		
		Map<Integer, List<String>> wordlength = wordCounts.getWordLength();
		
		System.out.println(wordlength);
		
		assertEquals(3, wordlength.get(new Integer(3)).size());
		assertEquals(7, wordlength.get(new Integer(4)).size());
		assertEquals(5, wordlength.get(new Integer(5)).size());
		
	}
	
	@Test
	public void getWordLength_Set() {
		Map<Integer, Collection<String>> wordlength = wordCounts.getWordLength_Set();		
		System.out.println(wordlength);
		assertEquals(3, wordlength.get(new Integer(3)).size());
		assertEquals(4, wordlength.get(new Integer(4)).size());
		assertEquals(3, wordlength.get(new Integer(5)).size());		
	}
	
	@Test
	public void getWordLength_List() {
		Map<Integer, List<String>> wordlength = wordCounts.getWordLength_List();
		System.out.println(wordlength);
	}
	
	@Test
	public void getWordLength_String() {		
		Map<Integer, String> wordlength = wordCounts.getWordLength_String();
		
		System.out.println(wordlength);
		assertEquals("[Tom|but|and]", wordlength.get(new Integer(3)));
		assertEquals("[John|beef|Mary|beef|beef|hate|beef]", wordlength.get(new Integer(4)));
		assertEquals("[likes|likes|likes|Allen|Jerry]", wordlength.get(new Integer(5)));
	}
	
	@Test
	public void getWordLength_summarizingInt() {
		Map<Integer, IntSummaryStatistics> rets = wordCounts.getWordLength_summarizingInt();		
		System.out.println(rets);
	}

}

4.3 SalesProductsTest

I will create test cases to test the SalesProducts.

SalesProductsTest.java

package com.zheng.demo;

import static org.junit.Assert.assertEquals;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import com.zheng.demo.domain.Fruit;
import com.zheng.demo.domain.FruitType;
import com.zheng.demo.domain.TypeQuantity;

public class SaleProductsTest {

	private SaleProducts salesItems;

	@Before
	public void setup() {
		salesItems = new SaleProducts(Arrays.asList(
				new Fruit(1, "Fuji", FruitType.APPLE, 10, new BigDecimal("10"), 3),
				new Fruit(2, "Apple Banana", FruitType.BANANA, 20, new BigDecimal("20"), 2),
				new Fruit(3, "Mandarin Orange", FruitType.ORGNGE, 10, new BigDecimal("30"), 4),
				new Fruit(4, "Seedless Watermelon", FruitType.WATERMELON, 10, new BigDecimal("30"), 2),
				new Fruit(5, "Papaya", FruitType.PAPAYA, 20, new BigDecimal("10"), 1),
				new Fruit(6, "Gala", FruitType.APPLE, 10, new BigDecimal("8"), 5),
				new Fruit(7, "Cuban Red Banana", FruitType.BANANA, 10, new BigDecimal("15"), 2),
				new Fruit(8, "Golden Delicious", FruitType.APPLE, 10, new BigDecimal("6"), 5)));
	}

	@Test
	public void groupByTypeWithCounts() {
		Map<FruitType, Long> groupByItems = salesItems.groupByTypeWithCounts();
		System.out.println("groupByTypeWithCounts: " + groupByItems);
		assertEquals(3, groupByItems.get(FruitType.APPLE).intValue());
		assertEquals(2, groupByItems.get(FruitType.BANANA).intValue());
		assertEquals(1, groupByItems.get(FruitType.ORGNGE).intValue());
		assertEquals(1, groupByItems.get(FruitType.PAPAYA).intValue());
		assertEquals(1, groupByItems.get(FruitType.WATERMELON).intValue());
	}
	

	@Test
	public void groupByType() {
		Map<FruitType, List<Fruit>> spCategores = salesItems.groupByType();
		System.out.println("groupByType: " + spCategores);
		assertEquals(1, spCategores.get(FruitType.PAPAYA).size());
		assertEquals(2, spCategores.get(FruitType.BANANA).size());
		assertEquals(3, spCategores.get(FruitType.APPLE).size());
	}

	@Test
	public void groupByLikes_lambda() {
		Map<FruitType, List<Fruit>> spCategores = salesItems.groupByType_lambda();
		System.out.println("groupByType: " + spCategores);
		assertEquals(1, spCategores.get(FruitType.PAPAYA).size());
		assertEquals(2, spCategores.get(FruitType.BANANA).size());
		assertEquals(3, spCategores.get(FruitType.APPLE).size());
	}
	@Test
	public void groupByLikes() {
		Map<Integer, List<Fruit>> groupByItems = salesItems.groupByLikes();
		System.out.println("groupByLikes: " + groupByItems);
		assertEquals(3, groupByItems.get(new Integer(2)).size());
		assertEquals(1, groupByItems.get(new Integer(4)).size());
		assertEquals(1, groupByItems.get(new Integer(3)).size());
		assertEquals(1, groupByItems.get(new Integer(1)).size());
		assertEquals(2, groupByItems.get(new Integer(5)).size());
	}
	@Test
	public void groupByTypeConcrrently() {
		Map<FruitType, List<Fruit>> groupByItems = salesItems.groupByTypeConcrrently();
		System.out.println("groupByTypeConcrrently: " + groupByItems);
		assertEquals(1, groupByItems.get(FruitType.PAPAYA).size());
		assertEquals(2, groupByItems.get(FruitType.BANANA).size());
		assertEquals(3, groupByItems.get(FruitType.APPLE).size());
	}

	@Test
	public void groupByTypeToSet() {
		Map<FruitType, Set<Fruit>> groupByItems = salesItems.groupByTypeToSet();
		System.out.println("groupByTypeConcrrently: " + groupByItems);
		assertEquals(1, groupByItems.get(FruitType.PAPAYA).size());
		assertEquals(2, groupByItems.get(FruitType.BANANA).size());
		assertEquals(3, groupByItems.get(FruitType.APPLE).size());

	}

	@Test
	public void groupByQuantity() {
		Map<Integer, List<Fruit>> groupByItems = salesItems.groupByQuantity();
		System.out.println("groupByQuantity: " + groupByItems);
		assertEquals(6, groupByItems.get(10).size());
		assertEquals(2, groupByItems.get(20).size());
	}

	@Test
	public void groupByLikesWithCounts() {
		Map<Integer, Long> groupByItems = salesItems.groupByLikesWithCounts();
		System.out.println("groupByLikesWithCounts: " + groupByItems);
		assertEquals(2, groupByItems.get(new Integer(5)).intValue());
		assertEquals(1, groupByItems.get(new Integer(4)).intValue());
		assertEquals(1, groupByItems.get(new Integer(3)).intValue());
		assertEquals(3, groupByItems.get(new Integer(2)).intValue());
		assertEquals(1, groupByItems.get(new Integer(1)).intValue());

	}

	@Test
	public void groupByTypeAndLike() {
		Map<String, List<Fruit>> groupByItems = salesItems.groupByTypeAndLike();
		System.out.println("groupByTypeAndLike: " + groupByItems);

		assertEquals(1, groupByItems.get(FruitType.PAPAYA.name() + 1).size());
		assertEquals(1, groupByItems.get(FruitType.APPLE.name() + 3).size());
		assertEquals(2, groupByItems.get(FruitType.APPLE.name() + 5).size());
		assertEquals(1, groupByItems.get(FruitType.ORGNGE.name() + 4).size());
		assertEquals(2, groupByItems.get(FruitType.BANANA.name() + 2).size());
	}

	@Test
	public void groupByTypeAndLike_2() {
		Map<FruitType, Map<Integer, List<Fruit>>> groupByItems = salesItems.groupByTypeAndLike_2();
		System.out.println("groupByTypeAndLike_2: " + groupByItems);
		assertEquals(1, groupByItems.get(FruitType.PAPAYA).get(new Integer(1)).size());
		assertEquals(1, groupByItems.get(FruitType.APPLE).get(new Integer(3)).size());
		assertEquals(2, groupByItems.get(FruitType.APPLE).get(new Integer(5)).size());
		assertEquals(1, groupByItems.get(FruitType.ORGNGE).get(new Integer(4)).size());
		assertEquals(2, groupByItems.get(FruitType.BANANA).get(new Integer(2)).size());
	}

	@Test
	public void groupByTypeQuantity() {
		Map<TypeQuantity, List<Fruit>> groupByItems = salesItems.groupByTypeQuantity();
		System.out.println("groupByTypeQuantity: " + groupByItems);
		assertEquals(1, groupByItems.get(new TypeQuantity(FruitType.PAPAYA, 20)).size());
		assertEquals(3, groupByItems.get(new TypeQuantity(FruitType.APPLE, 10)).size());
		assertEquals(1, groupByItems.get(new TypeQuantity(FruitType.BANANA, 10)).size());
		assertEquals(1, groupByItems.get(new TypeQuantity(FruitType.BANANA, 20)).size());
	}

	@Test
	public void groupByTypeWithQuantitySum() {
		Map<FruitType, Integer> groupByItems = salesItems.groupByTypeWithQuantitySum();
		System.out.println("groupByTypeWithQuantitySum" + groupByItems);
		assertEquals(30, groupByItems.get(FruitType.APPLE).intValue());
		assertEquals(30, groupByItems.get(FruitType.BANANA).intValue());
		assertEquals(10, groupByItems.get(FruitType.ORGNGE).intValue());
		assertEquals(20, groupByItems.get(FruitType.PAPAYA).intValue());
		assertEquals(10, groupByItems.get(FruitType.WATERMELON).intValue());
	}

	@Test
	public void groupByTypeWithTotalSum() {
		Map<FruitType, Double> groupByItems = salesItems.groupByTypeWithTotalSum();
		System.out.println("groupByTypeWithTotalSum" + groupByItems);
		assertEquals(240, groupByItems.get(FruitType.APPLE).intValue());
		assertEquals(550, groupByItems.get(FruitType.BANANA).intValue());
		assertEquals(300, groupByItems.get(FruitType.ORGNGE).intValue());
		assertEquals(200, groupByItems.get(FruitType.PAPAYA).intValue());
		assertEquals(300, groupByItems.get(FruitType.WATERMELON).intValue());
	}
	
	@Test
	public void groupByTypeWithAverageSum() {
		Map<FruitType, Double> groupByItems = salesItems.groupByTypeWithAverageSum();
		System.out.println("groupByTypeWithAverageSum" + groupByItems);
		assertEquals(80, groupByItems.get(FruitType.APPLE).intValue());
		assertEquals(275, groupByItems.get(FruitType.BANANA).intValue());
		assertEquals(300, groupByItems.get(FruitType.ORGNGE).intValue());
		assertEquals(200, groupByItems.get(FruitType.PAPAYA).intValue());
		assertEquals(300, groupByItems.get(FruitType.WATERMELON).intValue());
	}
	
	@Test
	public void groupByType_TotalSummary() {
		Map<FruitType, DoubleSummaryStatistics> groupByItems = salesItems.groupByType_TotalSummary();
		System.out.println("groupByType_TotalSummary" + groupByItems);
		
		assertEquals(3, groupByItems.get(FruitType.APPLE).getCount());
		assertEquals(240, groupByItems.get(FruitType.APPLE).getSum(),0);
		assertEquals(80, groupByItems.get(FruitType.APPLE).getAverage(),0);
		assertEquals(100, groupByItems.get(FruitType.APPLE).getMax(),0);
		assertEquals(60, groupByItems.get(FruitType.APPLE).getMin(),0);
	}

}

5. Demo

Execute mvn clean install and capture the output:

Test Output

C:\>cd gitworkspace\java8-demo

C:\gitworkspace\java8-demo>mvn clean install
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=512m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< zheng.jcg.demo:java8-demo >----------------------
[INFO] Building java8-demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ java8-demo ---
[INFO] Deleting C:\gitworkspace\java8-demo\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ java8-demo ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources,i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.3:compile (default-compile) @ java8-demo ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 6 source files to C:\gitworkspace\java8-demo\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ java8-demo ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources,i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.3:testCompile (default-testCompile) @ java8-demo ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 3 source files to C:\gitworkspace\java8-demo\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ java8-demo ---
[INFO] Surefire report directory: C:\gitworkspace\java8-demo\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.zheng.demo.GroupBy_PriorJDK8TestgroupByType_priorJava8: 
{PAPAYA=[Product [id=5, name=Papaya, type=PAPAYA, quantity=20, price=10, likes=1]], 
WATERMELON=[Product [id=4, name=Seedless Watermelon, type=WATERMELON, quantity=10, price=30, likes=2]], 
ORGNGE=[Product [id=3, name=Mandarin Orange, type=ORGNGE, quantity=10, price=30, likes=4]], 
BANANA=[Product[id=2, name=Apple Banana, type=BANANA, quantity=20, price=20, likes=2], Product[id=7, name=Cuban Red Banana, type=BANANA, quantity=10, price=15, likes=2]], 
APPLE=[Product [id=1, name=Fuji, type=APPLE, quantity=10, price=10, likes=3], Product [id=6, name=Gala, type=APPLE, quantity=10, price=8, likes=5], Product [id=8,
name=Golden Delicious, type=APPLE, quantity=10, price=6, likes=5]]}
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.072 sec

Running com.zheng.demo.SaleProductsTest
groupByTypeConcrrently: {PAPAYA=[Product [id=5, name=Papaya, type=PAPAYA, quanti
ty=20, price=10, likes=1]], WATERMELON=[Product [id=4, name=Seedless Watermelon,
 type=WATERMELON, quantity=10, price=30, likes=2]], ORGNGE=[Product [id=3, name=
Mandarin Orange, type=ORGNGE, quantity=10, price=30, likes=4]], BANANA=[Product
[id=2, name=Apple Banana, type=BANANA, quantity=20, price=20, likes=2], Product
[id=7, name=Cuban Red Banana, type=BANANA, quantity=10, price=15, likes=2]], APP
LE=[Product [id=8, name=Golden Delicious, type=APPLE, quantity=10, price=6, like
s=5], Product [id=6, name=Gala, type=APPLE, quantity=10, price=8, likes=5], Prod
uct [id=1, name=Fuji, type=APPLE, quantity=10, price=10, likes=3]]}
groupByType_TotalSummary{PAPAYA=DoubleSummaryStatistics{count=1, sum=200.000000,
 min=200.000000, average=200.000000, max=200.000000}, WATERMELON=DoubleSummarySt
atistics{count=1, sum=300.000000, min=300.000000, average=300.000000, max=300.00
0000}, ORGNGE=DoubleSummaryStatistics{count=1, sum=300.000000, min=300.000000, a
verage=300.000000, max=300.000000}, BANANA=DoubleSummaryStatistics{count=2, sum=
550.000000, min=150.000000, average=275.000000, max=400.000000}, APPLE=DoubleSum
maryStatistics{count=3, sum=240.000000, min=60.000000, average=80.000000, max=10
0.000000}}
groupByQuantity: {20=[Product [id=2, name=Apple Banana, type=BANANA, quantity=20
, price=20, likes=2], Product [id=5, name=Papaya, type=PAPAYA, quantity=20, pric
e=10, likes=1]], 10=[Product [id=1, name=Fuji, type=APPLE, quantity=10, price=10
, likes=3], Product [id=3, name=Mandarin Orange, type=ORGNGE, quantity=10, price
=30, likes=4], Product [id=4, name=Seedless Watermelon, type=WATERMELON, quantit
y=10, price=30, likes=2], Product [id=6, name=Gala, type=APPLE, quantity=10, pri
ce=8, likes=5], Product [id=7, name=Cuban Red Banana, type=BANANA, quantity=10,
price=15, likes=2], Product [id=8, name=Golden Delicious, type=APPLE, quantity=1
0, price=6, likes=5]]}
groupByTypeQuantity: {TypeQuantity [type=BANANA, quantity=20]=[Product [id=2, na
me=Apple Banana, type=BANANA, quantity=20, price=20, likes=2]], TypeQuantity [ty
pe=APPLE, quantity=10]=[Product [id=1, name=Fuji, type=APPLE, quantity=10, price
=10, likes=3], Product [id=6, name=Gala, type=APPLE, quantity=10, price=8, likes
=5], Product [id=8, name=Golden Delicious, type=APPLE, quantity=10, price=6, lik
es=5]], TypeQuantity [type=WATERMELON, quantity=10]=[Product [id=4, name=Seedles
s Watermelon, type=WATERMELON, quantity=10, price=30, likes=2]], TypeQuantity [t
ype=PAPAYA, quantity=20]=[Product [id=5, name=Papaya, type=PAPAYA, quantity=20,
price=10, likes=1]], TypeQuantity [type=BANANA, quantity=10]=[Product [id=7, nam
e=Cuban Red Banana, type=BANANA, quantity=10, price=15, likes=2]], TypeQuantity
[type=ORGNGE, quantity=10]=[Product [id=3, name=Mandarin Orange, type=ORGNGE, qu
antity=10, price=30, likes=4]]}
groupByType: {PAPAYA=[Product [id=5, name=Papaya, type=PAPAYA, quantity=20, pric
e=10, likes=1]], WATERMELON=[Product [id=4, name=Seedless Watermelon, type=WATER
MELON, quantity=10, price=30, likes=2]], ORGNGE=[Product [id=3, name=Mandarin Or
ange, type=ORGNGE, quantity=10, price=30, likes=4]], BANANA=[Product [id=2, name
=Apple Banana, type=BANANA, quantity=20, price=20, likes=2], Product [id=7, name
=Cuban Red Banana, type=BANANA, quantity=10, price=15, likes=2]], APPLE=[Product
 [id=1, name=Fuji, type=APPLE, quantity=10, price=10, likes=3], Product [id=6, n
ame=Gala, type=APPLE, quantity=10, price=8, likes=5], Product [id=8, name=Golden
 Delicious, type=APPLE, quantity=10, price=6, likes=5]]}
groupByTypeWithTotalSum{PAPAYA=200.0, WATERMELON=300.0, ORGNGE=300.0, BANANA=550
.0, APPLE=240.0}
groupByTypeWithQuantitySum{PAPAYA=20, WATERMELON=10, ORGNGE=10, BANANA=30, APPLE
=30}
groupByTypeWithCounts: {PAPAYA=1, WATERMELON=1, ORGNGE=1, BANANA=2, APPLE=3}
groupByLikes: {1=[Product [id=5, name=Papaya, type=PAPAYA, quantity=20, price=10
, likes=1]], 2=[Product [id=2, name=Apple Banana, type=BANANA, quantity=20, pric
e=20, likes=2], Product [id=4, name=Seedless Watermelon, type=WATERMELON, quanti
ty=10, price=30, likes=2], Product [id=7, name=Cuban Red Banana, type=BANANA, qu
antity=10, price=15, likes=2]], 3=[Product [id=1, name=Fuji, type=APPLE, quantit
y=10, price=10, likes=3]], 4=[Product [id=3, name=Mandarin Orange, type=ORGNGE,
quantity=10, price=30, likes=4]], 5=[Product [id=6, name=Gala, type=APPLE, quant
ity=10, price=8, likes=5], Product [id=8, name=Golden Delicious, type=APPLE, qua
ntity=10, price=6, likes=5]]}
groupByType: {PAPAYA=[Product [id=5, name=Papaya, type=PAPAYA, quantity=20, pric
e=10, likes=1]], WATERMELON=[Product [id=4, name=Seedless Watermelon, type=WATER
MELON, quantity=10, price=30, likes=2]], ORGNGE=[Product [id=3, name=Mandarin Or
ange, type=ORGNGE, quantity=10, price=30, likes=4]], BANANA=[Product [id=2, name
=Apple Banana, type=BANANA, quantity=20, price=20, likes=2], Product [id=7, name
=Cuban Red Banana, type=BANANA, quantity=10, price=15, likes=2]], APPLE=[Product
 [id=1, name=Fuji, type=APPLE, quantity=10, price=10, likes=3], Product [id=6, n
ame=Gala, type=APPLE, quantity=10, price=8, likes=5], Product [id=8, name=Golden
 Delicious, type=APPLE, quantity=10, price=6, likes=5]]}
groupByTypeAndLike: {PAPAYA1=[Product [id=5, name=Papaya, type=PAPAYA, quantity=
20, price=10, likes=1]], ORGNGE4=[Product [id=3, name=Mandarin Orange, type=ORGN
GE, quantity=10, price=30, likes=4]], APPLE5=[Product [id=6, name=Gala, type=APP
LE, quantity=10, price=8, likes=5], Product [id=8, name=Golden Delicious, type=A
PPLE, quantity=10, price=6, likes=5]], BANANA2=[Product [id=2, name=Apple Banana
, type=BANANA, quantity=20, price=20, likes=2], Product [id=7, name=Cuban Red Ba
nana, type=BANANA, quantity=10, price=15, likes=2]], WATERMELON2=[Product [id=4,
 name=Seedless Watermelon, type=WATERMELON, quantity=10, price=30, likes=2]], AP
PLE3=[Product [id=1, name=Fuji, type=APPLE, quantity=10, price=10, likes=3]]}
groupByTypeConcrrently: {PAPAYA=[Product [id=5, name=Papaya, type=PAPAYA, quanti
ty=20, price=10, likes=1]], WATERMELON=[Product [id=4, name=Seedless Watermelon,
 type=WATERMELON, quantity=10, price=30, likes=2]], ORGNGE=[Product [id=3, name=
Mandarin Orange, type=ORGNGE, quantity=10, price=30, likes=4]], BANANA=[Product
[id=2, name=Apple Banana, type=BANANA, quantity=20, price=20, likes=2], Product
[id=7, name=Cuban Red Banana, type=BANANA, quantity=10, price=15, likes=2]], APP
LE=[Product [id=8, name=Golden Delicious, type=APPLE, quantity=10, price=6, like
s=5], Product [id=6, name=Gala, type=APPLE, quantity=10, price=8, likes=5], Prod
uct [id=1, name=Fuji, type=APPLE, quantity=10, price=10, likes=3]]}
groupByLikesWithCounts: {1=1, 2=3, 3=1, 4=1, 5=2}
groupByTypeAndLike_2: {PAPAYA={1=[Product [id=5, name=Papaya, type=PAPAYA, quant
ity=20, price=10, likes=1]]}, WATERMELON={2=[Product [id=4, name=Seedless Waterm
elon, type=WATERMELON, quantity=10, price=30, likes=2]]}, ORGNGE={4=[Product [id
=3, name=Mandarin Orange, type=ORGNGE, quantity=10, price=30, likes=4]]}, BANANA
={2=[Product [id=2, name=Apple Banana, type=BANANA, quantity=20, price=20, likes
=2], Product [id=7, name=Cuban Red Banana, type=BANANA, quantity=10, price=15, l
ikes=2]]}, APPLE={3=[Product [id=1, name=Fuji, type=APPLE, quantity=10, price=10
, likes=3]], 5=[Product [id=6, name=Gala, type=APPLE, quantity=10, price=8, like
s=5], Product [id=8, name=Golden Delicious, type=APPLE, quantity=10, price=6, li
kes=5]]}}
groupByTypeWithAverageSum{PAPAYA=200.0, WATERMELON=300.0, ORGNGE=300.0, BANANA=2
75.0, APPLE=80.0}
Tests run: 15, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.293 sec

Running com.zheng.demo.WordCountsTest
{3=IntSummaryStatistics{count=3, sum=278922, min=84274, average=92974.000000, ma
x=97921}, 4=IntSummaryStatistics{count=7, sum=19979744, min=2314539, average=285
4249.142857, max=3195178}, 5=IntSummaryStatistics{count=5, sum=443739164, min=63
353322, average=88747832.800000, max=102974396}}
{3=[Tom|but|and], 4=[John|beef|Mary|beef|beef|hate|beef], 5=[likes|likes|likes|A
llen|Jerry]}
{3=[Tom, but, and], 4=[John, beef, Mary, beef, beef, hate, beef], 5=[likes, like
s, likes, Allen, Jerry]}
{3=[Tom, and, but], 4=[John, Mary, beef, hate], 5=[Allen, Jerry, likes]}
{but=1, Tom=1, and=1, John=1, hate=1, beef=4, Jerry=1, Allen=1, Mary=1, likes=3}

{3=[Tom, but, and], 4=[John, beef, Mary, beef, beef, hate, beef], 5=[likes, like
s, likes, Allen, Jerry]}
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.015 sec

Results :

Tests run: 22, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ java8-demo ---
[INFO] Building jar: C:\gitworkspace\java8-demo\target\java8-demo-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ java8-demo ---
[INFO] Installing C:\gitworkspace\java8-demo\target\java8-demo-0.0.1-SNAPSHOT.jar to C:\repo\zheng\jcg\demo\java8-demo\0.0.1-SNAPSHOT\java8-demo-0.0.1-SNAPSHOT.jar
[INFO] Installing C:\gitworkspace\java8-demo\pom.xml to C:\repo\zheng\jcg\demo\java8-demo\0.0.1-SNAPSHOT\java8-demo-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.611 s
[INFO] Finished at: 2018-12-06T08:45:09-06:00
[INFO] ------------------------------------------------------------------------

C:\gitworkspace\java8-demo>

6. Summary

In this example, we demonstrated how to use Java 8 Stream and Collector.groupingBy to group objects.

7. Download the Source Code

This example consists of a Maven project to group objects into a map using Java 8 Stream and Collector.groupingBy API.

Download
You can download the full source code of this example here: Java 8 Stream Collectors groupingBy Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ashley
1 year ago

Glad I came across this resource very insightful. Additionally, this resource of java might be helpful.

Back to top button