[Java] Stream流求和、排序、分组

发布时间 2023-07-26 16:09:27作者: 尽

List、Set集合通过Stream流求和

一、泛型为Integer、Long、Double、BigDecimal求和

Integer sum = scores.stream().reduce(Integer::sum).orElse(0);
Long sum = scores.stream().reduce(Long::sum).orElse(0L);
Double sum = scores.stream().reduce(Double::sum).orElse(0.00);
BigDecimal sum = scores.stream().reduce(BigDecimal::add).orElse(new
BigDecimal(0.00));

二、泛型为实体类

对单个属性求和

Integer sum = sales.stream().mapToInt(Sale::getOrderNum).sum();
Long sum = sales.stream().mapToLong(Sale::getOrderNum).sum();
Double sum = sales.stream().mapToDouble(Sale::getOrderNum).sum();
BigDecimal sum = sales.stream().map(Sale::getAppleSale).reduce(BigDecimal.ZERO,
BigDecimal::add);

对多个属性分别分组求和 并返回聚合后的对象

// 类型为BigDecimal
Sale result = sales.stream().reduce((x, y) -> newSale(x.getAppleSale().add(y.getAppleSale()),x.getBananaSale().add(y.getBananaSale()),x.getGrapeSale().add(y.getGrapeSale())))
.orElse(
new Sale(BigDecimal.ZERO, BigDecimal.ZERO,BigDecimal.ZERO)); // 类型为Integer、Long、Double(注:orElse中需输入对应类型初始值) Sale sale = sales.stream().reduce((x, y) -> new Sale(x.getAppleSale() + y.getAppleSale(), x.getBananaSale() + y.getBananaSale(), x.getGrapeSale() + y.getGrapeSale())) .orElse(new Sale(0.00, 0.00,0.00));

多字段乘积求和(基本数据类型)

int prices = list.stream().mapToInt(x-> x.getprice * x.getTotal).sum();

多字段乘积求和(BigDecimal)

BigDecimal prices = list.stream().map(x-> x.getPrice().multiply(new BigDecimal(x.getTotal()))).reduce(BigDecimal.ZERO, BigDecimal::add);

对对象中的多个字段求和时 如果对象中属性非常非常多 还像上边一样写不现实

解决办法“转为map 遍历赋值 下边json处理使用的事hutool 的json处理
//构造返回结果
SafeJwggaqhhsetj one = new SafeJwggaqhhsetj();
//把结果转为map方便赋值
Map<String, Object> resMap = JSONUtil.toBean(JSONUtil.toJsonStr(one), Map.class);
//要求和的对象的list集合
List<SafeJwggaqhhsetj> list = this.list(queryWrapper);
//将对象list转为json数组
JSONArray array = JSONUtil.parseArray(JSONUtil.toJsonStr(list));
//将对象list转为map数组
List<Map> dataMap = JSONUtil.toList(array, Map.class);
//遍历变量名集合 keylist可能是 Arrays.asList(new String[]
{"personAmountChinese", "personManhoursChinese", })
for (String s : keylist) {
//求和
  resMap.put(s, dataMap.stream().mapToInt(o -> {
  AtomicReference<Integer> d = new AtomicReference<>();
  Optional.ofNullable(o.get(s))
  .map(p -> {
    d.set((Integer) p);
    return p;
  })
  .orElseGet(() -> {
    d.set(0);
    return 0;
  });
  return d.get();
  }).sum());
}
one = JSONUtil.toBean(JSONUtil.toJsonStr(resMap), SafeJwggaqhhsetj.class);

对对象集合中的属性求和

单个bigdecimal属性的话直接 用map get 出来求和即可
例如 :
BigDecimal result2 = userList.stream()
// 将user对象的mongey取出来map为Bigdecimal
.map(User::getMoney)
// 使用reduce聚合函数,实现累加器
.reduce(BigDecimal.ZERO,BigDecimal::add);
此处为 将每个对象中多个属性求乘积以后再求和 
p.getSkuCount() * p.getVolumeLength().multiply(p.getVolumeHeight()).multiply(p.getVolumeWidth()).intValue()
并且此处需要返回的count 是integer类型 如果是bigdecimal 参考 对单个属性求和 中的bigdecimal 即
public static void main(String[] args) {
  List<ExtSkuLocationParam> locationAllSku = new ArrayList<>();
  for (int i = 1; i < 3; i++) {
    ExtSkuLocationParam a = new ExtSkuLocationParam();
    a.setSkuCount(i);
    a.setVolumeLength(new BigDecimal(i * 10));
    a.setVolumeWidth(new BigDecimal(i * 10));
    a.setVolumeHeight(new BigDecimal(i * 10));
    locationAllSku.add(a);
  }
  Integer reduce = locationAllSku.stream().reduce(0, (sum, p) -> sum += p.getSkuCount() * p.getVolumeLength().multiply(p.getVolumeHeight()).multiply(p.getVolumeWidth()).intValue(), Integer::sum);
  System.out.println(reduce);
}

只查找集合中符合条件的第一个元素并且返回

Map matchMap = nodeMapList.stream().filter(o -> o.get("id").equals(node.get("id"))).findFirst().get();

排序

直接排序数值

List<Integer> step = stepOrign.sorted().collect(Collectors.toList())

获取对象数组某个属性最小值那条记

bookList.stream().min(Comparator.comparing(Book::getSort)).get();

根据对象数组某个属性排序

ProjectApprovalGroup.get(id).stream().sorted(Comparator.comparing(ProjectApproval::getProcessStep)).collect(Collectors.toList());

排序List 的集合 集合内容是String 类型的数字

childrenArgList={"1","2","3"}
childrenArgList = childrenArgList.stream().sorted(Comparator.comparing(o->
Integer.parseInt((String) o)).reversed()).collect(Collectors.toList());
//此为按倒序排序
结果为{"3","2","1"}

多字段排序

public class User {
//学生id
private Integer userId;
//学生姓名
private String userName;
//学生年龄
private Integer age;
//学生班级
private Integer classNo;
}
List<User> userList = new ArrayList<>();
userList.add(new User(1, "shy01", 20, 1));
userList.add(new User(2, "shy02", 18, 3));
userList.add(new User(3, "shy03", 20, 4));
userList.add(new User(4, "shy04", 19, 2));
userList.add(new User(5, "shy05", 17, 5));
userList.add(new User(6, "shy06", 16, 4));
userList.add(new User(7, "shy07", 18, 9));
userList.add(new User(8, "shy08", 19, 8));
userList.add(new User(9, "shy09", 21, 7));

单个字段排序

根据年龄升序排序

userList = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
userList.forEach(System.out::println);

根据年龄降序排序

//方法1:先对年龄进行升序,结果进行反转
userList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
//方法2:直接对年龄进行降序
userList = userList.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder())).collect(Collectors.toList());
userList.forEach(System.out::println);

多个字段排序

年龄升序,班级升序

userList = userList.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getClassNo)).collect(Collectors.toList());

年龄降序,班级升序

//方法1:先对年龄进行升序,升序结果进行反转,再进行班级升序
userList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getClassNo)).collect(Collectors.toList());
//方法2:直接对年龄进行降序,再对班级进行升序
userList = userList.stream().sorted(Comparator.comparing(User::getAge,Comparator.reverseOrder()).thenComparing(User::getClassNo)).collect(Collectors.toList());

年龄降序,班级降序

//方法1:先对年龄进行升序,升序结果进行反转,再对班级进行降序
userList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getClassNo, Comparator.reverseOrder())).collect(Collectors.toList());
//方法2:直接对年龄进行降序,再对班级进行降序
userList = userList.stream().sorted(Comparator.comparing(User::getAge,Comparator.reverseOrder()).thenComparing(User::getClassNo,Comparator.reverseOrder())).collect(Collectors.toList());
//方式3:先对年龄进行升序,再对班级进行升序,最后对结果进行反转
userList = userList.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getClassNo).reversed()).collect(Collectors.toList());

年龄升序,班级降序

//方法1:先对年龄进行升序,升序结果进行反转,再进行班级升序,结果进行反转(有点绕,年龄被排了三次升-降-升)
userList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getClassNo).reversed()).collect(Collectors.toList());
//方法2:直接对年龄进行升序,再对班级进行降序
userList = userList.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getClassNo, Comparator.reverseOrder())).collect(Collectors.toList());

分组

多字段分组

自定义分组规则 u.getCity() + "|" + u.getSex()
public class Demo2 {
  public static void main(String[] args) {
    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss");
    // data list
    List<User> userList = Arrays.asList(
      User.builder().id(123456).name("Zhang,San").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-0112:00:00", df)).build(),
      User.builder().id(777777).name("Zhang,San").city("ShangHai").sex("woman").birthDay(LocalDateTime.parse("2022-07-0112:00:00", df)).build(),
      User.builder().id(888888).name("Li,Si").city("ShangHai").sex("man").birthDay(LocalDateTime.parse("2022-07-0112:00:00", df)).build(),
      User.builder().id(999999).name("Zhan,San").city("HangZhou").sex("woman").birthDay(LocalDateTime.parse("2022-07-0112:00:00", df)).build(),
      User.builder().id(555555).name("Li,Si").city("NaJin").sex("man").birthDay(LocalDateTime.parse("2022-07-0112:00:00", df)).build()
    );
    Map<String, List<User>> groupMap = userList.stream().collect(Collectors.groupingBy(u -> u.getCity() + "|" +u.getSex()));
    groupMap.forEach((k, v) -> {
      System.out.println(k);
      System.out.println(v);
    });
  }
}

格式化时间 分组

list.stream().collect(Collectors.groupingBy(item -> new SimpleDateFormat("yyyy-MM-dd HH").format(item.getCreateTime())));

stream对list进行分组,并对分组后的数据进行map操作  获取其中的某一项属性值

Map<String, List> map = list.stream().collect(Collectors.groupingBy(CourseTeacherDTO::getCourseId, Collectors.mapping(CourseTeacherDTO::getName, Collectors.toList())));

Java List集合Stream流按条件分组获取每组最大值

先根据时间分组,然后根据时间排序取最大
Map<String, ProjectReport> collect = a.stream().collect(Collectors.groupingBy( item->
new SimpleDateFormat("yyyy-MM").format(item.getCreateTime()),
Collectors.collectingAndThen(Collectors.reducing((c1,c2)
-> c1.getCreateTime().compareTo(c2.getCreateTime())>0 ? c1 : c2), Optional::get))); //此时map中每个键 只有 一个值 再将map 转为list集合 并按照时间倒序排序 List<ProjectReport> endepot = new ArrayList<>(collect.values()).stream().sorted(Comparator.comparing(ProjectReport::getCreateTime).reversed()).collect(Collectors.toList());

Java List集合Stream流按条件分组获取每组中第一个对象

如果要获取某个属性值 就value.get(0).getxxx

Map<Integer, Coupon> resultList = couponList.stream().collect(Collectors.groupingBy(Coupon::getCouponId, Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));

理论

  • 分区:将 stream 按条件分为两个 Map ,比如员工按薪资是否高于8000分为两部分。
  • 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
案例:将员工按薪资是否高于8000分为两部分;将员工按性别和地区分组
public class StreamTest {
  public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    personList.add(new Person("Tom", 8900, "male", "New York"));
    personList.add(new Person("Jack", 7000, "male", "Washington"));
    personList.add(new Person("Lily", 7800, "female", "Washington"));
    personList.add(new Person("Anni", 8200, "female", "New York"));
    personList.add(new Person("Owen", 9500, "male", "New York"));
    personList.add(new Person("Alisa", 7900, "female", "New York"));
    // 将员工按薪资是否高于8000分组
    Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
    // 将员工按性别分组
    Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
    // 将员工先按性别分组,再按地区分组
    Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
    System.out.println("员工按薪资是否大于8000分组情况:" + part);
    System.out.println("员工按性别分组情况:" + group);
    System.out.println("员工按性别、地区:" + group2);
  }
}
输出结果:
员工按薪资是否大于8000分组情况:{false=[mutest.Person@2d98a335, mutest.Person@16b98e56, mutest.Person@7ef20235], true=[mutest.Person@27d6c5e0, mutest.Person@4f3f5b24, mutest.Person@15aeb7ab]}
员工按性别分组情况:{female=[mutest.Person@16b98e56, mutest.Person@4f3f5b24, mutest.Person@7ef20235], male=[mutest.Person@27d6c5e0, mutest.Person@2d98a335, mutest.Person@15aeb7ab]}
员工按性别、地区:{female={New York=[mutest.Person@4f3f5b24, mutest.Person@7ef20235], Washington=[mutest.Person@16b98e56]}, male={New York=[mutest.Person@27d6c5e0, mutest.Person@15aeb7ab], Washington=[mutest.Person@2d98a335]}}