Collectors.groupingBy根据一个或多个属性对集合中的项目进行分组
数据准备:
@Datapublic class Product {private Long id;private Integer num;private BigDecimal price;private String name;private String category;public Product() {}public Product(Long id, Integer num, BigDecimal price, String name, String category) {this.id = id;this.num = num;this.price = price;this.name = name;this.category = category;}}
分组
按照类目分组:
Map<String, List<Product>> prodMap= prodList.stream().collect(Collectors.groupingBy(Product::getCategory));//{"啤酒":[{"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}]}
按照几个属性拼接分组:
Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "_" + item.getName()));//{"零食_月饼":[{"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}]}
根据不同条件分组
Map<String, List<Product>> prodMap= prodList.stream().collect(Collectors.groupingBy(item -> {if(item.getNum() < 3) {return "3";}else {return "other";}}));//{"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,并定义一个为流 中项目分类的二级标准。
Map<String, Map<String, List<Product>>> prodMap= prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.groupingBy(item -> {if(item.getNum() < 3) {return "3";}else {return "other";}})));//{"啤酒":{"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}]}}
按子组收集数据
求总数
Map<String, Long> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));//{"啤酒":2,"零食":3}
求和
Map<String, Integer> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));//{"啤酒":13,"零食":6}
排序
Map<String, Integer> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));prodMap.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).forEachOrdered(System.out::println);//{"啤酒":13,"零食":6}
把收集器的结果转换为另一种类型
Map<String, Product> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get)));//{"啤酒":{"category":"啤酒","id":5,"name":"百威啤酒","num":10,"price":15},"零食":{"category":"零食","id":3,"name":"月饼","num":3,"price":30}}
联合其他收集器
Map<String, Set<String>> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));//{"啤酒":["青岛啤酒","百威啤酒"],"零食":["面包","饼干","月饼"]}
案例:
import java.math.BigDecimal;import java.sql.Timestamp;public class Student {private String name;private Integer age;private String score;private Integer gender;private BigDecimal height;private Timestamp startTime;private Timestamp endTime;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getScore() {return score;}public void setScore(String score) {this.score = score;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}public BigDecimal getHeight() {return height;}public void setHeight(BigDecimal height) {this.height = height;}public Timestamp getStartTime() {return startTime;}public void setStartTime(Timestamp startTime) {this.startTime = startTime;}public Timestamp getEndTime() {return endTime;}public void setEndTime(Timestamp endTime) {this.endTime = endTime;}public Student(String name, Integer age) {this.name = name;this.age = age;}public Student(String name, Integer age, String score) {this.name = name;this.age = age;this.score = score;}public Student(String name, Integer age, String score, Integer gender) {this.name = name;this.age = age;this.score = score;this.gender = gender;}public Student(String name, Integer age, String score, Integer gender, BigDecimal height) {this.name = name;this.age = age;this.score = score;this.gender = gender;this.height = height;}public Student(String name, Integer age, String score, Integer gender, BigDecimal height, Timestamp startTime, Timestamp endTime) {this.name = name;this.age = age;this.score = score;this.gender = gender;this.height = height;this.startTime = startTime;this.endTime = endTime;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score='" + score + '\'' +", gender=" + gender +", height=" + height +", startTime=" + startTime +", endTime=" + endTime +'}';}}
import org.apache.commons.lang3.StringUtils;import javax.swing.tree.TreeModel;import java.io.Serializable;import java.math.BigDecimal;import java.sql.Timestamp;import java.time.LocalDateTime;import java.util.*;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.stream.Collectors;import java.util.stream.Stream;import static java.util.stream.Collectors.counting;import static java.util.stream.Collectors.toCollection;public class groupingBy {public static void main(String[] args) {ArrayList<Student> list = new ArrayList<>();list.add(new Student("王一", 21, "11", 1, new BigDecimal("193.0"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(15L))));list.add(new Student("郑二", 22, "22", 0, new BigDecimal("171.0"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusHours(16L))));list.add(new Student("张三", 23, "33", 1, new BigDecimal("180.1"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusSeconds(500L))));list.add(new Student("李四", 24, "44", 0, new BigDecimal("183.2"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(18L))));list.add(new Student("赵五", 25, "55", 1, new BigDecimal("178.3"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(13L))));list.add(new Student("韩六", 26, "88", 0, new BigDecimal("169.4"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(1L))));list.add(new Student("田七", 27, "77", 1, new BigDecimal("173.5"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(17L))));list.add(new Student("卢八", 28, "88", 0, new BigDecimal("159.6"), Timestamp.valueOf(LocalDateTime.now()), Timestamp.valueOf(LocalDateTime.now().plusMinutes(1L))));// list.add(new Student("钱九", 29, null, 1, new BigDecimal("180.1")));// list.add(new Student("魏十", null, "99", null, null));// list.add(new Student("肖十一", null, "", null, null));// list.add(new Student("柘十二", null, null, null, null));//1根据性别进行分组//1.1 为null的数据不需要,直接过滤Map<Integer, List<Student>> collect = list.stream().filter(a -> a.getGender() != null).collect(Collectors.groupingBy(Student::getGender));//1.2 需要保留为null的数据Map<? extends Serializable, List<Student>> collect1 = list.stream().collect(Collectors.groupingBy(a -> {if (a.getGender() == null) {return "null";}return a.getGender();}));//2根据性别分组后,再根据分数分组,>=60:A <60:B 为null的数据保留Map<? extends Serializable, Map<String, List<Student>>> collect2 = list.stream().collect(Collectors.groupingBy(a -> {if (a.getGender() == null) {return "null";}return a.getGender();}, Collectors.groupingBy(b -> {String score = b.getScore();if (score == null) {return "B";} else {return score.compareTo("60") >= 0 ? "A" : "B";}})));//3按照gender和height分组,TreeMap默认按key升序 TreeMap key不能为nullTreeMap<String, List<Student>> collect3 = list.stream().collect(Collectors.groupingBy(a -> {if (a.getGender() == null || a.getHeight() == null) {return "null";}return a.getGender() + "->" + a.getHeight();}, TreeMap::new, Collectors.toList()));//统计gender为1和0的人数Map<? extends Serializable, Long> collect4 = list.stream().collect(Collectors.groupingBy(a -> {if (a.getGender() == null) {return "mix";}return a.getGender();}, counting()));//按gender分组后,统计60为分界的人数Map<String, Map<String, Long>> collect5 = list.stream().collect(Collectors.groupingBy(//gender分组a -> {if (a.getGender() == null) {return "null";//gender为null}return a.getGender() == 1 ? "M" : "F";}, Collectors.groupingBy(//60为分界b -> {String score = b.getScore();if (score == null) {return "null";} else {return score.compareTo("60") >= 0 ? "A" : "B";}}, counting()//统计)));//统计60分界的男女各多少Map<String, Map<Integer, Long>> collect6 = list.stream().filter(a -> StringUtils.isNoneBlank(a.getScore()))//过滤score为""和null的数据.filter(b -> b.getGender() != null)//过滤gender为null的数据.collect(Collectors.groupingBy(c -> c.getScore().compareTo("60") >= 0 ? "G" : "B",//以60分组Collectors.groupingBy(Student::getGender,//以gender分组counting()//统计)));//统计男女各年龄之和Map<Integer, Integer> collect7 = list.stream().filter(a -> a.getAge() != null || a.getGender() != null).collect(Collectors.groupingBy(Student::getGender,Collectors.summingInt(Student::getAge)));//分组元素存储在自定义集合Map<Integer, TreeSet<Integer>> collect8 = list.stream().filter(a -> a.getGender() != null).collect(Collectors.groupingBy(Student::getGender,Collectors.mapping(Student::getAge, toCollection(TreeSet::new))));//计算男女中最高的分数Map<Integer, Student> collect9 = list.stream().filter(a -> a.getGender() != null).collect(Collectors.groupingBy(Student::getGender, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Student::getScore)), Optional::get)));//计算男女中最低的分数Map<Integer, Student> collect10 = list.stream().filter(a -> a.getGender() != null).collect(Collectors.toMap(Student::getGender, Function.identity(), BinaryOperator.minBy(Comparator.comparing(Student::getScore))));// //计算男女中最高的分数 分数相同去用时最短Map<Integer, Student> collect11 = list.stream().filter(a -> a.getGender() != null).collect(Collectors.toMap(Student::getGender,//性别Function.identity(),//t->tBinaryOperator.maxBy(Comparator.comparing(Student::getScore)//分数比较.thenComparing(b -> b.getEndTime().getTime() - b.getStartTime().getTime()//分数相同,按时间比较, Comparator.reverseOrder()))//外层用的max,故此处排序取反));//相同的存放在list中TreeMap<Integer, Student> collect12 = list.stream().filter(a -> a.getGender() != null).collect(Collectors.toMap(Student::getGender,Function.identity(),BinaryOperator.maxBy(Comparator.comparing(Student::getScore).thenComparing(b -> b.getEndTime().getTime() - b.getStartTime().getTime(), Comparator.reverseOrder())),TreeMap::new));HashMap<Integer, List<Student>> hashMap = new HashMap<>();collect12.forEach((k, v) -> {Map<Integer, ArrayList<Student>> listMap = list.stream().filter(a ->k.equals(a.getGender())&& v.getScore().equals(a.getScore())&& (a.getEndTime().getTime() - a.getStartTime().getTime()) == (v.getEndTime().getTime() - v.getStartTime().getTime())).collect(Collectors.toMap(Student::getGender,t -> {List<Student> students = Collections.singletonList(t);return new ArrayList<>(students);},(v1, v2) -> {v2.addAll(v1);return v2;}));hashMap.putAll(listMap);});//分数最高用时最短Optional<Student> max = list.stream().max(Comparator.comparing(Student::getScore).thenComparing(a -> a.getEndTime().getTime() - a.getStartTime().getTime(),Comparator.reverseOrder()));Student student = max.isPresent() ? max.get() : null;//最大值 分数 用时Student student1 = list.stream().max(Comparator.comparing(Student::getScore).thenComparing(a -> a.getEndTime().getTime() - a.getStartTime().getTime(),Comparator.reverseOrder())).orElse(null);//男女各组的平均分数Map<Integer, Double> collect13 = list.stream().filter(a -> a.getGender() != null).filter(b -> StringUtils.isNoneBlank(b.getScore())).collect(Collectors.groupingBy(Student::getGender,Collectors.averagingDouble(a -> Double.parseDouble(a.getScore()))));}}
