欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > Java8使用Stream流实现List列表查询、统计、排序、分组、合并

Java8使用Stream流实现List列表查询、统计、排序、分组、合并

2024/10/24 15:22:18 来源:https://blog.csdn.net/qq_26383975/article/details/139859872  浏览:    关键词:Java8使用Stream流实现List列表查询、统计、排序、分组、合并

Java8使用Stream流实现List列表查询、统计、排序以及分组

目录

  • 一、查询方法
    • 1.1 forEach
    • 1.2 filter(T -> boolean)
    • 1.3 filterAny() 和 filterFirst()
    • 1.4 map(T -> R) 和 flatMap(T -> Stream)
    • 1.5 distinct()
    • 1.6 limit(long n) 和 skip(long n)
  • 二、判断方法
    • 2.1 anyMatch(T -> boolean)
    • 2.2 allMatch(T -> boolean)
    • 2.3 noneMatch(T -> boolean)
  • 三、统计方法
    • 3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
    • 3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long)
    • 3.3 使用 counting() 或 count()
    • 3.4 summingInt() 、summingLong()、summingDouble()
    • 3.5 averagingInt() 、averagingLong()、averagingDouble()
    • 3.6 summarizingInt()、summarizingLong()、summarizingDouble()
    • 3.7 BigDecimal类型的统计
  • 四、排序方法
    • 4.1 sorted() / sorted((T,T) -> int)
  • 五、分组方法
    • 5.1 groupingBy
    • 5.2 多级分组
    • 5.3 分组汇总
  • 六、List的合并
    • 6.1 并集
    • 6.2 交集

List的 Stream流操作可以简化我们的代码,减少程序运行的压力,应对上面的问题,下面这篇文章主要给大家介绍了关于 Java8使用Stream流实现List列表查询、统计、排序以及分组的相关资料,需要的朋友可以参考下

Java8提供了Stream(流)处理集合的关键抽象概念,它可以对集合进行操作,可以执行非常复杂的查找、过滤和映射数据等操作。

Stream API 借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。

下面是使用Stream的常用方法的综合实例。

创建User类作为持久层

package com.example.demo.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import java.math.BigDecimal;/*** @author qzz* @date 2023/3/21*/
@Data
@AllArgsConstructor
public class User {/*** 用户id*/private Integer id;/*** 名称*/private String name;/*** sex*/private String sex;/*** 年龄*/private Integer age;/*** 部门*/private String department;/*** 薪资*/private BigDecimal salary;
}

创建UserService.class 用户信息业务逻辑类

package com.example.demo.service;import com.example.demo.entity.User;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;/*** 用户信息业务逻辑类* @author qzz* @date 2024/6/21*/
@Service
public class UserService {/*** 获取用户列表* @return*/public List<User> getUserList(){List<User> userList = new ArrayList<>();userList.add(new User(1,"张三","男",26, "研发部", BigDecimal.valueOf(3000)));userList.add(new User(2,"李斯","男",30, "财务部", BigDecimal.valueOf(2000)));userList.add(new User(3,"张倩","女",26, "人事部", BigDecimal.valueOf(1600)));userList.add(new User(4,"吴昕","女",30, "研发部", BigDecimal.valueOf(2000)));userList.add(new User(5,"刘希","女",26, "财务部", BigDecimal.valueOf(1300)));return userList;}
}

一、查询方法

1.1 forEach

使用forEach()遍历列表信息。

    /*** 1.使用forEach()遍历列表信息*/@Testpublic void forEachTest(){//获取用户列表List<User> userList = userService.getUserList();//遍历用户列表 方法一:userList.forEach(user -> {System.out.println(user);});System.out.println();//遍历用户列表 方法二:     函数式接口 变量名 = 类实例::方法名userList.forEach(System.out::println);}

控制台输出:
在这里插入图片描述

1.2 filter(T -> boolean)

使用filter()过滤列表数据。

【示例】获取部门为’研发部’的用户列表

    /*** 2.使用filter()过滤列表数据*/@Testpublic void filterTest(){//获取用户列表List<User> userList = userService.getUserList();//获取部门为'研发部'的用户列表userList = userList.stream().filter(user -> user.getDepartment().equals("研发部")).collect(Collectors.toList());//遍历用户列表userList.forEach(System.out::println);}

控制台输出:
在这里插入图片描述

1.3 filterAny() 和 filterFirst()

使用 filterAny() 和 filterFirst() 获取第一条数据。

    /*** 3.filterAny()和filterFirst() :获取第一条数据*/@Testpublic void filterAnyTest(){//获取用户列表List<User> userList = userService.getUserList();//获取用户名称为'张三'的用户信息,如果没有找到则返回nullUser user = userList.stream().filter(u -> u.getName().equals("张三")).findAny().orElse(null);//输出用户信息System.out.println(user);}

控制台输出:
在这里插入图片描述
注意:findFirst() 和 findAny() 都是获取列表中的第一条数据,但是findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。

如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行(parallelStream并行流)的情况,那就不能确保是第一个。

例如:使用parallelStream并行流,findAny() 返回的就不一定是第一条数据

//parallelStream方法能生成并行流,使用filterAny返回的 不一定是第一条数据
User user = userList.parallelStream().filter(u -> u.getName().startsWith("wsq")).findAny().orElse(null);

1.4 map(T -> R) 和 flatMap(T -> Stream)

使用map()将流中的每一个元素 T 映射为 R(类似类型转换)
使用flatmap()将流中的每一个元素 T 映射为 一个流,再把每一个流连接成为一个流。

【示例】使用map()方法获取用户列表中的名称列。

    /*** 4.1使用map()获取列元素*/@Testpublic void mapTest(){//获取用户列表List<User> userList = userService.getUserList();//获取用户名称列表List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
//        或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());//输出用户名称信息nameList.forEach(System.out::println);}

控制台输出:
在这里插入图片描述
【示例】使用flatmap()方法将流中的每一个元素连接成为一个流。

    /*** 4.2使用flatMap()将流中的每一个元素连接成为一个流*/@Testpublic void flatMapTest(){//创建城市List<String> cityList = new ArrayList<>();cityList.add("北京;上海;深圳;");cityList.add("广州;武汉;杭州;");//分隔城市列表,使用 flatMap()将流中的每一个元素连接成为一个流cityList = cityList.stream().map(city -> city.split(";")).flatMap(Arrays::stream).collect(Collectors.toList());//输出城市列表cityList.forEach(System.out::println);}

控制台输出:
在这里插入图片描述

1.5 distinct()

使用 distinct()方法可以去除重复的数据。

【示例】获取部门列表,并去除重复数据

    /*** 5.distinct()去除重复的数据*/@Testpublic void distinct(){//获取用户列表List<User> userList = userService.getUserList();//获取部门列表,并去除重复数据List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());//输出部门列表departmentList.forEach(System.out::println);}

控制台输出:
在这里插入图片描述

1.6 limit(long n) 和 skip(long n)

limit(long n) 方法用于返回前n条数据,skip(long n)方法用于跳过前n条数据。

【示例】获取用户列表,要求跳过第1条数据后的前3条数据

/*** 6.limit(long n) 和 skip(long n)*/@Testpublic void limitAndSkipTest(){//获取用户列表List<User> userList = userService.getUserList();//获取用户列表,要求 跳过第一条数据后的前3条数数据userList = userList.stream().skip(1).limit(3).collect(Collectors.toList());//输出用户列表userList.forEach(System.out::println);}

控制台输出:
在这里插入图片描述

二、判断方法

2.1 anyMatch(T -> boolean)

使用 anyMatch(T -> boolean) 判断流中是否 有一个元素 匹配给定的 T -> boolean 条件

2.2 allMatch(T -> boolean)

使用 allMatch(T -> boolean) 判断流中是否 所有元素 都匹配给定的 T -> boolean 条件。

2.3 noneMatch(T -> boolean)

使用 noneMatch(T -> boolean) 流中是否 没有元素 匹配给定的 T -> boolean 条件。

【示例】使用 anyMatch()、allMatch()、noneMatch() 进行判断。

    /*** 使用 anyMatch()、allMatch()、noneMatch() 进行判断*/@Testpublic void matchTest(){//获取用户列表List<User> userList = userService.getUserList();//判断用户列表中 是否存在 名称为”张三“ 的 数据boolean result1 = userList.stream().anyMatch(user -> user.getName().equals("张三"));//判断用户名称   是否都包含”张三“ 的 数据boolean result2 = userList.stream().allMatch(user -> user.getName().contains("张三"));//判断用户名称   是否存在都不包含”张三“ 的 数据boolean result3 = userList.stream().noneMatch(user -> user.getName().contains("张三"));//输出用户列表System.out.println(result1);System.out.println(result2);System.out.println(result3);}

控制台输出:
在这里插入图片描述

三、统计方法

3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)

使用 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T) 用于组合流中的元素,如求和,求积,求最大值等。

【示例】使用 reduce() 求用户列表中年龄的最大值、最小值、总和

    /*** 使用 reduce() 求用户列表中年龄的最大值、最小值、总和*/@Testpublic void reduceTest(){//获取用户列表List<User> userList = userService.getUserList();//用户列表中年龄的 最大值、最小值、总和int maxVal = userList.stream().map(User::getAge).reduce(Integer::max).get();int minVal = userList.stream().map(User::getAge).reduce(Integer::min).get();int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);//打印结果System.out.println("最大年龄:" + maxVal);System.out.println("最小年龄:" + minVal);System.out.println("年龄总和:" + sumVal);}

控制台输出:
在这里插入图片描述

3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long)

int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);计算元素总和的方法其中暗含了装箱成本,map(User::getAge) 方法过后流变成了 Stream 类型,而每个 Integer 都要拆箱成一个原始类型再进行 sum 方法求和,这样大大影响了效率。

针对这个问题 Java 8 有良心地引入了数值流 IntStream, DoubleStream, LongStream,这种流中的元素都是原始数据类型,分别是 int,double,long

流转换为数值流

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream

【示例】使用 mapToInt() 求用户列表中年龄的最大值、最小值、总和、平均值

/*** 使用 mapToInt() 求用户列表中年龄的最大值、最小值、总和、平均值*/@Testpublic void mapToIntTest(){//获取用户列表List<User> userList = userService.getUserList();//用户列表中年龄的 最大值、最小值、总和、平均值int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt();int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt();int sumVal = userList.stream().mapToInt(User::getAge).sum();double aveVal = userList.stream().mapToInt(User::getAge).average().getAsDouble();//打印结果System.out.println("最大年龄:" + maxVal);System.out.println("最小年龄:" + minVal);System.out.println("年龄总和:" + sumVal);System.out.println("平均年龄:" + aveVal);}

控制台输出:
在这里插入图片描述

3.3 使用 counting() 或 count()

使用 counting() 或 count() 可以对列表数据进行统计。

【示例】使用 count() 统计用户列表信息。

    /*** 使用 counting() 或 count() 统计*/@Testpublic void countTest(){//获取用户列表List<User> userList = userService.getUserList();//统计研发部的人数,使用counting()方法进行统计Long departCount = userList.stream().filter(user -> user.getDepartment().equals("研发部")).collect(Collectors.counting());//统计30岁以上的人数,使用count()方法进行统计(推荐)Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();//统计薪资大于1500元以上的人数Long salaryCount = userList.stream().filter(user -> user.getSalary().compareTo(BigDecimal.valueOf(1500)) == 1).count();//打印结果System.out.println("研发部的人数:" + departCount + "人");System.out.println("30岁以上的人数:" + ageCount + "人");System.out.println("薪资大于1500元以上的人数:" + salaryCount + "人");}

控制台输出:
在这里插入图片描述

3.4 summingInt() 、summingLong()、summingDouble()

用于计算总和,需要一个函数参数

    /*** 使用 summingInt() 、summingLong()、summingDouble() 计算总和*/@Testpublic void sumTest(){//获取用户列表List<User> userList = userService.getUserList();//计算年龄总和int sunAge = userList.stream().collect(Collectors.summingInt(User::getAge));//打印结果System.out.println("年龄总和:" + sunAge);}

控制台输出:
在这里插入图片描述

3.5 averagingInt() 、averagingLong()、averagingDouble()

用于计算平均值

/*** 使用 averagingInt() 、averagingLong()、averagingDouble() 计算平均值*/@Testpublic void averagingTest(){//获取用户列表List<User> userList = userService.getUserList();//计算平均年龄Double aveAge = userList.stream().collect(Collectors.averagingInt(User::getAge));//打印结果System.out.println("平均年龄:" + aveAge);}

控制台输出:
在这里插入图片描述

3.6 summarizingInt()、summarizingLong()、summarizingDouble()

这三个方法比较特殊,比如 summarizingInt 会返回 IntSummaryStatistics 类型

IntSummaryStatistics类提供了用于计算的平均值、总数、最大值、最小值、总和等方法
方法如下图:

    /*** 使用 IntSummaryStatistics 统计:最大值、最小值、总和、平均值、总数*/@Testpublic void summarizingIntTest(){//获取用户列表List<User> userList = userService.getUserList();//获取IntSummaryStatistics对象IntSummaryStatistics ageStatistics = userList.stream().collect(Collectors.summarizingInt(User::getAge));//统计:最大值、最小值、总和、平均值、总数System.out.println("最大年龄:" + ageStatistics.getMax());System.out.println("最小年龄:" + ageStatistics.getMin());System.out.println("年龄总和:" + ageStatistics.getSum());System.out.println("平均年龄:" + ageStatistics.getAverage());System.out.println("员工总数:" + ageStatistics.getCount());}

控制台输出:
在这里插入图片描述

3.7 BigDecimal类型的统计

对于资金相关的字段,通常会使用BigDecimal数据类型

【示例】统计用户薪资信息

    /*** BigDecimal类型的统计*/@Testpublic void bigDecimalTest(){//获取用户列表List<User> userList = userService.getUserList();//最高薪资BigDecimal maxSalary = userList.stream().map(User::getSalary).max((x1,x2) -> x1.compareTo(x2)).get();//最低薪资BigDecimal minSalary = userList.stream().map(User::getSalary).min((x1,x2) -> x1.compareTo(x2)).get();//薪资总和BigDecimal sumSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);//平均薪资BigDecimal aveSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(userList.size()), 2, BigDecimal.ROUND_HALF_UP);//打印统计结果System.out.println("最高薪资: " + maxSalary + "元");System.out.println("最低薪资: " + minSalary + "元");System.out.println("薪资总和: " + sumSalary + "元");System.out.println("平均薪资: " + aveSalary + "元");}

控制台输出:
在这里插入图片描述

四、排序方法

4.1 sorted() / sorted((T,T) -> int)

如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,如 Stream。反之, 需要调用 sorted((T, T) -> int) 实现 Comparator 接口

【示例】根据用户年龄进行排序。

/*** 使用 sorted() 排序*/
@Test
public void sortedTest(){//获取用户列表List<User> userList = userService.getUserList();//根据年龄排序(升序)userList = userList.stream().sorted((u1,u2) -> u1.getAge() - u2.getAge()).collect(Collectors.toList());//推荐(升序):userList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());//降序:userList = userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList());//遍历用户列表userList.forEach(System.out::println);
}

控制台输出:
在这里插入图片描述

五、分组方法

5.1 groupingBy

使用 groupingBy() 将数据进行分组,最终返回一个 Map 类型。

【示例】根据部门对用户列表进行分组

/*** 使用 groupingBy() 分组*/@Testpublic void groupingByTest(){//获取用户列表List<User> userList = userService.getUserList();//根据部门对用户列表进行分组Map<String, List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment));//遍历分组后的结果userMap.forEach((key,value) -> {System.out.println(key + ": ");value.forEach(System.out::println);System.out.println("-----------------------------------------------------");});}

控制台输出:
在这里插入图片描述

5.2 多级分组

groupingBy 可以接受一个第二参数实现多级分组。

【示例】根据部门和性别对用户列表进行分组。

    /*** 使用 groupingBy() 多级分组*/@Testpublic void multGroupingByTest(){//获取用户列表List<User> userList = userService.getUserList();//根据部门和性别对用户列表进行分组Map<String, Map<String,List<User>>> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment,Collectors.groupingBy(User::getSex)));//遍历分组后的结果userMap.forEach((key1,map) -> {System.out.println(key1 + ": ");map.forEach((key2,user)->{System.out.println(key2 + ": ");user.forEach(System.out::println);});System.out.println("-----------------------------------------------------");});}

控制台输出:
在这里插入图片描述

5.3 分组汇总

【示例】根据部门进行分组,汇总各个部门用户的平均年龄。

    /*** 使用 groupingBy() 分组汇总*/@Testpublic void groupingByCollectTest(){//获取用户列表List<User> userList = userService.getUserList();//根据部门进行分组,汇总各个部门用户的平均年龄Map<String, Double> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment, Collectors.averagingInt(User::getAge)));//遍历分组后的结果userMap.forEach((key,value) -> {System.out.println(key + "的平均年龄: " + value);});}

控制台输出:
在这里插入图片描述

六、List的合并

6.1 并集

    /*** 得到两个集合的并集*/@Testpublic void unionTest(){List<String> list1 = new ArrayList<>();list1.add("aaa");list1.add("bbb");list1.add("ccc");list1.add("ddd");List<String> list2 = new ArrayList<>();list2.add("aaa");list2.add("ccc");list2.add("ddd");list2.add("eee");list2.add("fff");list2.add("aaa1");list2.add("aaa2");//根据整个对象是否一致来去重合并得到集合System.out.println("并集写法 [去重]");List<String> collect = Stream.of(list1, list2).flatMap(Collection::stream).distinct().collect(Collectors.toList());System.out.println(collect.toString());}

6.2 交集

    /*** 得到两个集合的交集*/@Testpublic void unionTest(){List<String> list1 = new ArrayList<>();list1.add("aaa");list1.add("bbb");list1.add("ccc");list1.add("ddd");List<String> list2 = new ArrayList<>();list2.add("aaa");list2.add("ccc");list2.add("ddd");list2.add("eee");list2.add("fff");list2.add("aaa1");list2.add("aaa2");System.out.println("交集写法 [去重]");List<String> collect1 = list1.stream().filter(s-> list2.contains(s)).distinct().collect(Collectors.toList());System.out.println(collect1.toString());}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com