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.
| Return | Method |
|---|---|
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>> | 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 likesgroupByLikesWithCounts– group by the likes, return its countsgroupByQuantity– group by the quantitygroupByType– group by the typegroupByTypeAndLike– group by both type and likes using a methodgroupB yTypeAndLike_2– group by both type and likes using twogroupingBymethodsgroupByTypeConcurrently– group by the type concurrentlygroupByTypeQuantity– group by the type and quantity with aTypeQuantityclassgroupByTypeSet– group by the type, return it as a setgroupByTypeWithAverageSum– group by the type, return the average of the total costgroupByTypeWithQuantitySum– group by the type, return the total quantitygroupByTypeWithTotalSum– 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.
You can download the full source code of this example here: Java 8 Stream Collectors groupingBy Example



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