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 String
s 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 twogroupingBy
methodsgroupByTypeConcurrently
– group by the type concurrentlygroupByTypeQuantity
– group by the type and quantity with aTypeQuantity
classgroupByTypeSet
– 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.