Collectors.groupingBy根据一个或多个属性对集合中的项目进行分组

数据准备:

  1. @Data
  2. public class Product {
  3. private Long id;
  4. private Integer num;
  5. private BigDecimal price;
  6. private String name;
  7. private String category;
  8. public Product() {
  9. }
  10. public Product(Long id, Integer num, BigDecimal price, String name, String category) {
  11. this.id = id;
  12. this.num = num;
  13. this.price = price;
  14. this.name = name;
  15. this.category = category;
  16. }
  17. }

分组

按照类目分组:

  1. Map<String, List<Product>> prodMap= prodList.stream().collect(Collectors.groupingBy(Product::getCategory));
  2. //{"啤酒":[{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10},{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}],"零食":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5},{"category":"零食","id":2,"name":"饼干","num":2,"price":20},{"category":"零食","id":3,"name":"月饼","num":3,"price":30}]}

按照几个属性拼接分组:

  1. Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "_" + item.getName()));
  2. //{"零食_月饼":[{"category":"零食","id":3,"name":"月饼","num":3,"price":30}],"零食_面包":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5}],"啤酒_百威啤酒":[{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}],"啤酒_青岛啤酒":[{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10}],"零食_饼干":[{"category":"零食","id":2,"name":"饼干","num":2,"price":20}]}

根据不同条件分组

  1. Map<String, List<Product>> prodMap= prodList.stream().collect(Collectors.groupingBy(item -> {
  2. if(item.getNum() < 3) {
  3. return "3";
  4. }else {
  5. return "other";
  6. }
  7. }));
  8. //{"other":[{"category":"零食","id":3,"name":"月饼","num":3,"price":30},{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10},{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}],"3":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5},{"category":"零食","id":2,"name":"饼干","num":2,"price":20}]}

多级分组

要实现多级分组,我们可以使用一个由双参数版本的Collectors.groupingBy工厂方法创 建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。那么要进 行二级分组的话,我们可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流 中项目分类的二级标准。

  1. Map<String, Map<String, List<Product>>> prodMap= prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.groupingBy(item -> {
  2. if(item.getNum() < 3) {
  3. return "3";
  4. }else {
  5. return "other";
  6. }
  7. })));
  8. //{"啤酒":{"other":[{"category":"啤酒","id":4,"name":"青岛啤酒","num":3,"price":10},{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15}]},"零食":{"other":[{"category":"零食","id":3,"name":"月饼","num":3,"price":30}],"3":[{"category":"零食","id":1,"name":"面包","num":1,"price":15.5},{"category":"零食","id":2,"name":"饼干","num":2,"price":20}]}}

按子组收集数据

求总数
  1. Map<String, Long> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));
  2. //{"啤酒":2,"零食":3}

求和
  1. Map<String, Integer> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));
  2. //{"啤酒":13,"零食":6}

排序
  1. Map<String, Integer> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));
  2. prodMap.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
  3. .forEachOrdered(System.out::println);
  4. //{"啤酒":13,"零食":6}

把收集器的结果转换为另一种类型
  1. Map<String, Product> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get)));
  2. //{"啤酒":{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15},"零食":{"category":"零食","id":3,"name":"月饼","num":3,"price":30}}

联合其他收集器
  1. Map<String, Set<String>> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));
  2. //{"啤酒":["青岛啤酒","百威啤酒"],"零食":["面包","饼干","月饼"]}


案例:

  1. import java.math.BigDecimal;
  2. import java.sql.Timestamp;
  3. public class Student {
  4. private String name;
  5. private Integer age;
  6. private String score;
  7. private Integer gender;
  8. private BigDecimal height;
  9. private Timestamp startTime;
  10. private Timestamp endTime;
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public Integer getAge() {
  18. return age;
  19. }
  20. public void setAge(Integer age) {
  21. this.age = age;
  22. }
  23. public String getScore() {
  24. return score;
  25. }
  26. public void setScore(String score) {
  27. this.score = score;
  28. }
  29. public Integer getGender() {
  30. return gender;
  31. }
  32. public void setGender(Integer gender) {
  33. this.gender = gender;
  34. }
  35. public BigDecimal getHeight() {
  36. return height;
  37. }
  38. public void setHeight(BigDecimal height) {
  39. this.height = height;
  40. }
  41. public Timestamp getStartTime() {
  42. return startTime;
  43. }
  44. public void setStartTime(Timestamp startTime) {
  45. this.startTime = startTime;
  46. }
  47. public Timestamp getEndTime() {
  48. return endTime;
  49. }
  50. public void setEndTime(Timestamp endTime) {
  51. this.endTime = endTime;
  52. }
  53. public Student(String name, Integer age) {
  54. this.name = name;
  55. this.age = age;
  56. }
  57. public Student(String name, Integer age, String score) {
  58. this.name = name;
  59. this.age = age;
  60. this.score = score;
  61. }
  62. public Student(String name, Integer age, String score, Integer gender) {
  63. this.name = name;
  64. this.age = age;
  65. this.score = score;
  66. this.gender = gender;
  67. }
  68. public Student(String name, Integer age, String score, Integer gender, BigDecimal height) {
  69. this.name = name;
  70. this.age = age;
  71. this.score = score;
  72. this.gender = gender;
  73. this.height = height;
  74. }
  75. public Student(String name, Integer age, String score, Integer gender, BigDecimal height, Timestamp startTime, Timestamp endTime) {
  76. this.name = name;
  77. this.age = age;
  78. this.score = score;
  79. this.gender = gender;
  80. this.height = height;
  81. this.startTime = startTime;
  82. this.endTime = endTime;
  83. }
  84. @Override
  85. public String toString() {
  86. return "Student{" +
  87. "name='" + name + '\'' +
  88. ", age=" + age +
  89. ", score='" + score + '\'' +
  90. ", gender=" + gender +
  91. ", height=" + height +
  92. ", startTime=" + startTime +
  93. ", endTime=" + endTime +
  94. '}';
  95. }
  96. }
  1. import org.apache.commons.lang3.StringUtils;
  2. import javax.swing.tree.TreeModel;
  3. import java.io.Serializable;
  4. import java.math.BigDecimal;
  5. import java.sql.Timestamp;
  6. import java.time.LocalDateTime;
  7. import java.util.*;
  8. import java.util.function.BinaryOperator;
  9. import java.util.function.Function;
  10. import java.util.stream.Collectors;
  11. import java.util.stream.Stream;
  12. import static java.util.stream.Collectors.counting;
  13. import static java.util.stream.Collectors.toCollection;
  14. public class groupingBy {
  15. public static void main(String[] args) {
  16. ArrayList<Student> list = new ArrayList<>();
  17. list.add(new Student("王一", 21, "11", 1, new BigDecimal("193.0"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(15L))));
  18. list.add(new Student("郑二", 22, "22", 0, new BigDecimal("171.0"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusHours(16L))));
  19. list.add(new Student("张三", 23, "33", 1, new BigDecimal("180.1"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusSeconds(500L))));
  20. list.add(new Student("李四", 24, "44", 0, new BigDecimal("183.2"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(18L))));
  21. list.add(new Student("赵五", 25, "55", 1, new BigDecimal("178.3"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(13L))));
  22. list.add(new Student("韩六", 26, "88", 0, new BigDecimal("169.4"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(1L))));
  23. list.add(new Student("田七", 27, "77", 1, new BigDecimal("173.5"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(17L))));
  24. list.add(new Student("卢八", 28, "88", 0, new BigDecimal("159.6"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(1L))));
  25. // list.add(new Student("钱九", 29, null, 1, new BigDecimal("180.1")));
  26. // list.add(new Student("魏十", null, "99", null, null));
  27. // list.add(new Student("肖十一", null, "", null, null));
  28. // list.add(new Student("柘十二", null, null, null, null));
  29. //1根据性别进行分组
  30. //1.1 为null的数据不需要,直接过滤
  31. Map<Integer, List<Student>> collect = list.stream()
  32. .filter(a -> a.getGender() != null)
  33. .collect(Collectors.groupingBy(Student::getGender));
  34. //1.2 需要保留为null的数据
  35. Map<? extends Serializable, List<Student>> collect1 = list.stream()
  36. .collect(Collectors.groupingBy(a -> {
  37. if (a.getGender() == null) {
  38. return "null";
  39. }
  40. return a.getGender();
  41. }));
  42. //2根据性别分组后,再根据分数分组,>=60:A <60:B 为null的数据保留
  43. Map<? extends Serializable, Map<String, List<Student>>> collect2 = list.stream().collect(Collectors.groupingBy(a -> {
  44. if (a.getGender() == null) {
  45. return "null";
  46. }
  47. return a.getGender();
  48. }, Collectors.groupingBy(b -> {
  49. String score = b.getScore();
  50. if (score == null) {
  51. return "B";
  52. } else {
  53. return score.compareTo("60") >= 0 ? "A" : "B";
  54. }
  55. })));
  56. //3按照gender和height分组,TreeMap默认按key升序 TreeMap key不能为null
  57. TreeMap<String, List<Student>> collect3 = list.stream().collect(Collectors.groupingBy(a -> {
  58. if (a.getGender() == null || a.getHeight() == null) {
  59. return "null";
  60. }
  61. return a.getGender() + "->" + a.getHeight();
  62. }
  63. , TreeMap::new
  64. , Collectors.toList()));
  65. //统计gender为1和0的人数
  66. Map<? extends Serializable, Long> collect4 = list.stream()
  67. .collect(Collectors.groupingBy(a -> {
  68. if (a.getGender() == null) {
  69. return "mix";
  70. }
  71. return a.getGender();
  72. }
  73. , counting()));
  74. //按gender分组后,统计60为分界的人数
  75. Map<String, Map<String, Long>> collect5 = list.stream().collect(
  76. Collectors.groupingBy(//gender分组
  77. a -> {
  78. if (a.getGender() == null) {
  79. return "null";//gender为null
  80. }
  81. return a.getGender() == 1 ? "M" : "F";
  82. }
  83. , Collectors.groupingBy(//60为分界
  84. b -> {
  85. String score = b.getScore();
  86. if (score == null) {
  87. return "null";
  88. } else {
  89. return score.compareTo("60") >= 0 ? "A" : "B";
  90. }
  91. }
  92. , counting()//统计
  93. )
  94. ));
  95. //统计60分界的男女各多少
  96. Map<String, Map<Integer, Long>> collect6 = list.stream()
  97. .filter(a -> StringUtils.isNoneBlank(a.getScore()))//过滤score为""和null的数据
  98. .filter(b -> b.getGender() != null)//过滤gender为null的数据
  99. .collect(
  100. Collectors.groupingBy(
  101. c -> c.getScore().compareTo("60") >= 0 ? "G" : "B",//以60分组
  102. Collectors.groupingBy(
  103. Student::getGender,//以gender分组
  104. counting()//统计
  105. )
  106. )
  107. );
  108. //统计男女各年龄之和
  109. Map<Integer, Integer> collect7 = list.stream()
  110. .filter(a -> a.getAge() != null || a.getGender() != null)
  111. .collect(Collectors.groupingBy(Student::getGender,
  112. Collectors.summingInt(Student::getAge)));
  113. //分组元素存储在自定义集合
  114. Map<Integer, TreeSet<Integer>> collect8 = list.stream()
  115. .filter(a -> a.getGender() != null)
  116. .collect(Collectors.groupingBy(Student::getGender,
  117. Collectors.mapping(Student::getAge, toCollection(TreeSet::new))));
  118. //计算男女中最高的分数
  119. Map<Integer, Student> collect9 = list.stream().filter(a -> a.getGender() != null)
  120. .collect(Collectors.groupingBy(
  121. Student::getGender, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Student::getScore)), Optional::get)
  122. )
  123. );
  124. //计算男女中最低的分数
  125. Map<Integer, Student> collect10 = list.stream().filter(a -> a.getGender() != null)
  126. .collect(Collectors.toMap(Student::getGender, Function.identity(), BinaryOperator.minBy(Comparator.comparing(Student::getScore)))
  127. );
  128. // //计算男女中最高的分数 分数相同去用时最短
  129. Map<Integer, Student> collect11 = list.stream().filter(a -> a.getGender() != null)
  130. .collect(Collectors.toMap(
  131. Student::getGender,//性别
  132. Function.identity(),//t->t
  133. BinaryOperator.maxBy(
  134. Comparator.comparing(Student::getScore)//分数比较
  135. .thenComparing(b -> b.getEndTime().getTime() - b.getStartTime().getTime()//分数相同,按时间比较
  136. , Comparator.reverseOrder()))//外层用的max,故此处排序取反
  137. )
  138. );
  139. //相同的存放在list中
  140. TreeMap<Integer, Student> collect12 = list.stream().filter(a -> a.getGender() != null)
  141. .collect(Collectors.toMap(
  142. Student::getGender,
  143. Function.identity(),
  144. BinaryOperator.maxBy(
  145. Comparator.comparing(Student::getScore)
  146. .thenComparing(b -> b.getEndTime().getTime() - b.getStartTime().getTime()
  147. , Comparator.reverseOrder())
  148. ),
  149. TreeMap::new
  150. )
  151. );
  152. HashMap<Integer, List<Student>> hashMap = new HashMap<>();
  153. collect12.forEach((k, v) -> {
  154. Map<Integer, ArrayList<Student>> listMap = list.stream()
  155. .filter(a ->
  156. k.equals(a.getGender())
  157. && v.getScore().equals(a.getScore())
  158. && (a.getEndTime().getTime() - a.getStartTime().getTime()) == (v.getEndTime().getTime() - v.getStartTime().getTime())
  159. )
  160. .collect(Collectors.toMap(
  161. Student::getGender,
  162. t -> {
  163. List<Student> students = Collections.singletonList(t);
  164. return new ArrayList<>(students);
  165. },
  166. (v1, v2) -> {
  167. v2.addAll(v1);
  168. return v2;
  169. })
  170. );
  171. hashMap.putAll(listMap);
  172. });
  173. //分数最高用时最短
  174. Optional<Student> max = list.stream().max(Comparator
  175. .comparing(Student::getScore)
  176. .thenComparing(a -> a.getEndTime().getTime() - a.getStartTime().getTime(),
  177. Comparator.reverseOrder())
  178. );
  179. Student student = max.isPresent() ? max.get() : null;
  180. //最大值 分数 用时
  181. Student student1 = list.stream().max(Comparator
  182. .comparing(Student::getScore)
  183. .thenComparing(a -> a.getEndTime().getTime() - a.getStartTime().getTime(),
  184. Comparator.reverseOrder())
  185. ).orElse(null);
  186. //男女各组的平均分数
  187. Map<Integer, Double> collect13 = list.stream()
  188. .filter(a -> a.getGender() != null)
  189. .filter(b -> StringUtils.isNoneBlank(b.getScore()))
  190. .collect(Collectors.groupingBy(Student::getGender,
  191. Collectors.averagingDouble(a -> Double.parseDouble(a.getScore()))));
  192. }
  193. }