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


     

    https://blog.csdn.net/pan_junbiao/article/details/105913518

    Java8提供了Stream(流)处理集合的关键抽象概念,它可以对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。Stream API 借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。

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

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

    1.  
      import com.pjb.streamdemo.entity.User;
    2.  
      import java.math.BigDecimal;
    3.  
      import java.util.ArrayList;
    4.  
      import java.util.List;
    5.  
       
    6.  
      /**
    7.  
      * 用户信息业务逻辑类
    8.  
      * @author pan_junbiao
    9.  
      **/
    10.  
      public class UserService
    11.  
      {
    12.  
      /**
    13.  
      * 获取用户列表
    14.  
      */
    15.  
      public static List<User> getUserList()
    16.  
      {
    17.  
      List<User> userList = new ArrayList<User>();
    18.  
      userList.add(new User(1, "pan_junbiao的博客_01", "男", 32, "研发部", BigDecimal.valueOf(1600)));
    19.  
      userList.add(new User(2, "pan_junbiao的博客_02", "男", 30, "财务部", BigDecimal.valueOf(1800)));
    20.  
      userList.add(new User(3, "pan_junbiao的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
    21.  
      userList.add(new User(4, "pan_junbiao的博客_04", "男", 38, "研发部", BigDecimal.valueOf(1500)));
    22.  
      userList.add(new User(5, "pan_junbiao的博客_05", "女", 25, "财务部", BigDecimal.valueOf(1200)));
    23.  
      return userList;
    24.  
      }
    25.  
      }

    1、查询方法

    1.1 forEach()

    使用 forEach() 遍历列表数据。

    1.  
      /**
    2.  
      * 使用forEach()遍历列表信息
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void forEachTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //遍历用户列表
    12.  
      userList.forEach(System.out::println);
    13.  
      }

    上述遍历语句等同于以下语句:

    userList.forEach(user -> {System.out.println(user);});

    执行结果:

    1.2 filter(T -> boolean)

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

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

    1.  
      /**
    2.  
      * 使用filter()过滤列表信息
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void filterTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //获取部门为“研发部”的用户列表
    12.  
      userList = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.toList());
    13.  
       
    14.  
      //遍历用户列表
    15.  
      userList.forEach(System.out::println);
    16.  
      }

    执行结果:

    1.3 findAny() 和 findFirst()

    使用 findAny() 和 findFirst() 获取第一条数据。

    【示例】获取用户名称为“pan_junbiao的博客_02”的用户信息,如果未找到则返回null。

    1.  
      /**
    2.  
      * 使用findAny()获取第一条数据
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void findAnytTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //获取用户名称为“pan_junbiao的博客_02”的用户信息,如果没有找到则返回null
    12.  
      User user = userList.stream().filter(u -> u.getName().equals("pan_junbiao的博客_02")).findAny().orElse(null);
    13.  
       
    14.  
      //打印用户信息
    15.  
      System.out.println(user);
    16.  
      }

    执行结果:

    注意:findFirst() 和 findAny() 都是获取列表中的第一条数据,但是findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行(parallelStream并行流)的情况,那就不能确保是第一个。

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

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

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

    使用 map() 将流中的每一个元素 T 映射为 R(类似类型转换)。

    使用 flatMap() 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流。

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

    1.  
      /**
    2.  
      * 使用map()获取列元素
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void mapTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //获取用户名称列表
    12.  
      List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
    13.  
      //或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());
    14.  
       
    15.  
      //遍历名称列表
    16.  
      nameList.forEach(System.out::println);
    17.  
      }

     返回的结果为数组类型,写法如下:

    1.  
      //数组类型
    2.  
      String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray(new String[userList.size()]);

     执行结果:

    【示例】使用 flatMap() 将流中的每一个元素连接成为一个流。

    1.  
      /**
    2.  
      * 使用flatMap()将流中的每一个元素连接成为一个流
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void flatMapTest()
    7.  
      {
    8.  
      //创建城市
    9.  
      List<String> cityList = new ArrayList<String>();
    10.  
      cityList.add("北京;上海;深圳;");
    11.  
      cityList.add("广州;武汉;杭州;");
    12.  
       
    13.  
      //分隔城市列表,使用 flatMap() 将流中的每一个元素连接成为一个流。
    14.  
      cityList = cityList.stream()
    15.  
      .map(city -> city.split(";"))
    16.  
      .flatMap(Arrays::stream)
    17.  
      .collect(Collectors.toList());
    18.  
       
    19.  
      //遍历城市列表
    20.  
      cityList.forEach(System.out::println);
    21.  
      }

    执行结果:

    1.5 distinct()

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

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

    1.  
      /**
    2.  
      * 使用distinct()去除重复数据
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void distinctTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //获取部门列表,并去除重复数据
    12.  
      List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());
    13.  
       
    14.  
      //遍历部门列表
    15.  
      departmentList.forEach(System.out::println);
    16.  
      }

    执行结果:

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

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

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

    1.  
      /**
    2.  
      * limit(long n)方法用于返回前n条数据
    3.  
      * skip(long n)方法用于跳过前n条数据
    4.  
      * @author pan_junbiao
    5.  
      */
    6.  
      @Test
    7.  
      public void limitAndSkipTest()
    8.  
      {
    9.  
      //获取用户列表
    10.  
      List<User> userList = UserService.getUserList();
    11.  
       
    12.  
      //获取用户列表,要求跳过第1条数据后的前3条数据
    13.  
      userList = userList.stream()
    14.  
      .skip(1)
    15.  
      .limit(3)
    16.  
      .collect(Collectors.toList());
    17.  
       
    18.  
      //遍历用户列表
    19.  
      userList.forEach(System.out::println);
    20.  
      }

    执行结果:

    2、判断方法

    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() 进行判断。

    1.  
      /**
    2.  
      * 使用 anyMatch()、allMatch()、noneMatch() 进行判断
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void matchTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //判断用户列表中是否存在名称为“pan_junbiao的博客_01”的数据
    12.  
      boolean result1 = userList.stream().anyMatch(user -> user.getName().equals("pan_junbiao的博客_01"));
    13.  
       
    14.  
      //判断用户名称是否都包含“pan_junbiao的博客”字段
    15.  
      boolean result2 = userList.stream().allMatch(user -> user.getName().contains("pan_junbiao的博客"));
    16.  
       
    17.  
      //判断用户名称是否存在不包含“pan_junbiao的博客”字段
    18.  
      boolean result3 = userList.stream().noneMatch(user -> user.getName().contains("pan_junbiao的博客"));
    19.  
       
    20.  
      //打印结果
    21.  
      System.out.println(result1);
    22.  
      System.out.println(result2);
    23.  
      System.out.println(result3);
    24.  
      }

    执行结果:

    3、统计方法

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

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

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

    1.  
      /**
    2.  
      * 使用 reduce() 方法
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void reduceTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //用户列表中年龄的最大值、最小值、总和
    12.  
      int maxVal = userList.stream().map(User::getAge).reduce(Integer::max).get();
    13.  
      int minVal = userList.stream().map(User::getAge).reduce(Integer::min).get();
    14.  
      int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);
    15.  
       
    16.  
      //打印结果
    17.  
      System.out.println("最大年龄:" + maxVal);
    18.  
      System.out.println("最小年龄:" + minVal);
    19.  
      System.out.println("年龄总和:" + sumVal);
    20.  
      }

    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() 求用户列表中年龄的最大值、最小值、总和、平均值。

    1.  
      /**
    2.  
      * 使用 mapToInt() 方法
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void mapToIntTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //用户列表中年龄的最大值、最小值、总和、平均值
    12.  
      int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt();
    13.  
      int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt();
    14.  
      int sumVal = userList.stream().mapToInt(User::getAge).sum();
    15.  
      double aveVal = userList.stream().mapToInt(User::getAge).average().getAsDouble();
    16.  
       
    17.  
      //打印结果
    18.  
      System.out.println("最大年龄:" + maxVal);
    19.  
      System.out.println("最小年龄:" + minVal);
    20.  
      System.out.println("年龄总和:" + sumVal);
    21.  
      System.out.println("平均年龄:" + aveVal);
    22.  
      }

    执行结果:

    3.3 counting() 和 count()

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

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

    1.  
      /**
    2.  
      * 使用 counting() 或 count() 统计
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void countTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //统计研发部的人数,使用 counting()方法进行统计
    12.  
      Long departCount = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.counting());
    13.  
       
    14.  
      //统计30岁以上的人数,使用 count()方法进行统计(推荐)
    15.  
      Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();
    16.  
       
    17.  
      //统计薪资大于1500元的人数
    18.  
      Long salaryCount = userList.stream().filter(user -> user.getSalary().compareTo(BigDecimal.valueOf(1500)) == 1).count();
    19.  
       
    20.  
      //打印结果
    21.  
      System.out.println("研发部的人数:" + departCount + "人");
    22.  
      System.out.println("30岁以上的人数:" + ageCount + "人");
    23.  
      System.out.println("薪资大于1500元的人数:" + salaryCount + "人");
    24.  
      }

    执行结果:

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

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

    1.  
      //计算年龄总和
    2.  
      int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));

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

    用于计算平均值。

    1.  
      //计算平均年龄
    2.  
      double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));

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

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

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

    【示例】使用 IntSummaryStatistics 统计:最大值、最小值、总和、平均值、总数。

    1.  
      /**
    2.  
      * 使用 summarizingInt 统计
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void summarizingIntTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //获取IntSummaryStatistics对象
    12.  
      IntSummaryStatistics ageStatistics = userList.stream().collect(Collectors.summarizingInt(User::getAge));
    13.  
       
    14.  
      //统计:最大值、最小值、总和、平均值、总数
    15.  
      System.out.println("最大年龄:" + ageStatistics.getMax());
    16.  
      System.out.println("最小年龄:" + ageStatistics.getMin());
    17.  
      System.out.println("年龄总和:" + ageStatistics.getSum());
    18.  
      System.out.println("平均年龄:" + ageStatistics.getAverage());
    19.  
      System.out.println("员工总数:" + ageStatistics.getCount());
    20.  
      }

    执行结果:

    3.7 BigDecimal类型的统计

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

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

    1.  
      /**
    2.  
      * BigDecimal类型的统计
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void BigDecimalTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //最高薪资
    12.  
      BigDecimal maxSalary = userList.stream().map(User::getSalary).max((x1, x2) -> x1.compareTo(x2)).get();
    13.  
       
    14.  
      //最低薪资
    15.  
      BigDecimal minSalary = userList.stream().map(User::getSalary).min((x1, x2) -> x1.compareTo(x2)).get();
    16.  
       
    17.  
      //薪资总和
    18.  
      BigDecimal sumSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
    19.  
       
    20.  
      //平均薪资
    21.  
      BigDecimal avgSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(userList.size()), 2, BigDecimal.ROUND_HALF_UP);
    22.  
       
    23.  
      //打印统计结果
    24.  
      System.out.println("最高薪资:" + maxSalary + "元");
    25.  
      System.out.println("最低薪资:" + minSalary + "元");
    26.  
      System.out.println("薪资总和:" + sumSalary + "元");
    27.  
      System.out.println("平均薪资:" + avgSalary + "元");
    28.  
      }

    执行结果:

     4、排序方法

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

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

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

    1.  
      /**
    2.  
      * 使用 sorted() 排序
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void sortedTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //根据年龄排序(升序)
    12.  
      userList = userList.stream().sorted((u1, u2) -> u1.getAge() - u2.getAge()).collect(Collectors.toList());
    13.  
      //推荐:userList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());
    14.  
      //降序:userList = userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList());
    15.  
       
    16.  
      //遍历用户列表
    17.  
      userList.forEach(System.out::println);
    18.  
      }

    推荐使用如下写法:

    1.  
      //升序
    2.  
      userList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());
    3.  
       
    4.  
      //降序
    5.  
      userList = userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList());

    执行结果:

    5、分组方法

    5.1 groupingBy

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

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

    1.  
      /**
    2.  
      * 使用 groupingBy() 分组
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void groupingByTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //根据部门对用户列表进行分组
    12.  
      Map<String,List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment));
    13.  
       
    14.  
      //遍历分组后的结果
    15.  
      userMap.forEach((key, value) -> {
    16.  
      System.out.println(key + ":");
    17.  
      value.forEach(System.out::println);
    18.  
      System.out.println("--------------------------------------------------------------------------");
    19.  
      });
    20.  
      }

    执行结果:

    5.2 多级分组

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

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

    1.  
      /**
    2.  
      * 使用 groupingBy() 多级分组
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void multGroupingByTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //根据部门和性别对用户列表进行分组
    12.  
      Map<String,Map<String,List<User>>> userMap = userList.stream()
    13.  
      .collect(Collectors.groupingBy(User::getDepartment,Collectors.groupingBy(User::getSex)));
    14.  
       
    15.  
      //遍历分组后的结果
    16.  
      userMap.forEach((key1, map) -> {
    17.  
      System.out.println(key1 + ":");
    18.  
      map.forEach((key2,user)->
    19.  
      {
    20.  
      System.out.println(key2 + ":");
    21.  
      user.forEach(System.out::println);
    22.  
      });
    23.  
      System.out.println("--------------------------------------------------------------------------");
    24.  
      });
    25.  
      }

    执行结果:

    5.3 分组汇总

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

    1.  
      /**
    2.  
      * 使用 groupingBy() 分组汇总
    3.  
      * @author pan_junbiao
    4.  
      */
    5.  
      @Test
    6.  
      public void groupCollectTest()
    7.  
      {
    8.  
      //获取用户列表
    9.  
      List<User> userList = UserService.getUserList();
    10.  
       
    11.  
      //根据部门进行分组,汇总各个部门用户的平均年龄
    12.  
      Map<String, Double> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment, Collectors.averagingInt(User::getAge)));
    13.  
       
    14.  
      //遍历分组后的结果
    15.  
      userMap.forEach((key, value) -> {
    16.  
      System.out.println(key + "的平均年龄:" + value);
    17.  
      });
    18.  
      }

    执行结果:

  • 相关阅读:
    jq ajax之beforesend(XHR)
    WPF string,color,brush之间的转换
    wpf后台设置颜色(背景色,前景色)
    VMWare之——宿主机与虚拟机互相ping通,宿主机ping通另一台机器的虚拟机
    动态SQL的执行,注:exec sp_executesql 其实可以实现参数查询和输出参数的
    【SQLSERVER】动态游标的实现
    减小Delphi 2010/delphi XE编译出来的文件大小
    正确理解 SqlConnection 的连接池机制[转]
    关于VS2005中C#代码用F12转到定义时,总是显示从元数据的问题
    通过cmd命令安装、卸载、启动和停止Windows Service(InstallUtil.exe)
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/14406683.html
Copyright © 2020-2023  润新知