Collectors.groupingBy根据一个或多个属性对集合中的项目进行分组
数据准备:
@Data
public 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;
}
@Override
public 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不能为null
TreeMap<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->t
BinaryOperator.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()))));
}
}