• Stream流 - 聚合操作和遍历操作


    上转 《Stream流 - 获取Stream和转换操作》

    聚合操作

    将最终处理的结果进行聚合输出。

    • 聚合操作
      • min/max:最值操作,需要比较器
      • count:统计操作,统计数据个数
      • collect:收集操作,使用官方的Collectors提供的收集器
      • findFirst/findAny:查找操作,返回的类型为Optional
      • noneMatch、AllMatch和anyMatch:匹配操作,检查数据流中是否存在符合条件的数据,返回一个boolean值
      • reduce:规约操作,将数据流的值规约为一个值,例如count/min/max底层实际上就是使用reduce
      • forEach:遍历操作,可以对最终的数据进行消费
      • toArray:数组操作,用于将数据流的元素转换为数组

    min和max

    min和max用于获取最小值和最大值,实现 Comparator接口

    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(39);
    list.add(10);
    list.add(78);
    list.add(39);
    
    Integer maxInteger = list.stream().max(Integer::compareTo).get();
    
    System.out.println(maxInteger);
    
    输出:
    78
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    count

    count统计数据个数。

    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(39);
    list.add(10);
    list.add(78);
    list.add(39);
    
    long len = list.stream().count();
    
    System.out.println(len);
    
    输出:
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    collect

    Collectors类实现了很多的聚合操作,例如将流转换为集合或者聚合元素,Collector可以返回集合、字符串、Map集合。

    返回集合、字符串

    List<String> list1 = Arrays.asList("abc","","bcd","","efg","abcd","jklm");
    
    List<String> list2 = list1.stream().filter(str->!str.isEmpty()).collect(Collectors.toList());
    String result = list1.stream().filter(str->!str.isEmpty()).collect(Collectors.joining(","));
    
    System.out.println(list2);
    System.out.println(result);
    
    输出:
    [abc, bcd, efg, abcd, jklm]
    abc,bcd,efg,abcd,jklm
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    返回Map集合

    使用 Collectors.toMap 方法将集合中的元素收集到Map中,但是要求有2个参数,分别用来生成Map中的key值和value值。

    List<Product> list = new ArrayList<>();
    list.add(new Product("product1","11.2"));
    list.add(new Product("product2","22.3"));
    list.add(new Product("product3","33.4"));
    
    // 以对象的id值作为key,存储的value为name属性值
    Map<String, String> map1 = list.stream().collect(Collectors.toMap(Product::getName, Product::getPrice));
    System.out.println(map1);
    
    // Function.identity()用于获取实际的对象
    Map<String, Product> map2 = list.stream().collect(Collectors.toMap(Product::getName, Function.identity()));
    System.out.println(map2);
    
    输出:
    {product2=22.3, product1=11.2, product3=33.4}
    {product2=Product{name='product2', price='22.3'}, product1=Product{name='product1', price='11.2'}, product3=Product{name='product3', price='33.4'}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Function.identity()用于获取实际的对象。

    分组

    按字段分组

    Collectors.groupingBy() 指定分组字段

    List<Product> list = new ArrayList<>();
    list.add(new Product("product1","11.2"));
    list.add(new Product("product2","22.3"));
    list.add(new Product("product3","33.4"));
    list.add(new Product("product4","11.2"));
    
    Map<String, List<Product>> map = list.stream().collect(Collectors.groupingBy(Product::getPrice));
    System.out.println(map);
    
    输出:
    {33.4=[Product{name='product3', price='33.4'}], 22.3=[Product{name='product2', price='22.3'}], 11.2=[Product{name='product1', price='11.2'}, Product{name='product4', price='11.2'}]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    按条件分组

    Collectors.groupingBy() 指定分组条件,当分组条件是一个返回boolean值的函数时,流元素可以分为2组列表,一个是返回true的元素集合,一个是返回false的元素集合。

    List<Product> list = new ArrayList<>();
    list.add(new Product("product1","11.2"));
    list.add(new Product("product2","22.3"));
    list.add(new Product("product3","33.4"));
    list.add(new Product("product4","11.2"));
    
    //将大于20岁的人分为一组,将不大于20岁的人分为一组 
    Map<Boolean, List<Product>> map = list.stream().collect(Collectors.groupingBy(v -> Double.valueOf(v.getPrice()) > 20));
    System.out.println(map);
    System.out.println(map.get(true));
    
    Map<Boolean, List<Product>> map2 = list.stream().collect(Collectors.partitioningBy(v -> Double.valueOf(v.getPrice()) > 20));
    System.out.println(map2);
    
    输出:
    {false=[Product{name='product1', price='11.2'}, Product{name='product4', price='11.2'}], true=[Product{name='product2', price='22.3'}, Product{name='product3', price='33.4'}]}
    [Product{name='product2', price='22.3'}, Product{name='product3', price='33.4'}]
    {false=[Product{name='product1', price='11.2'}, Product{name='product4', price='11.2'}], true=[Product{name='product2', price='22.3'}, Product{name='product3', price='33.4'}]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Collectors.partitioningBy()也可以分组。

    Stream分组并排序:介绍Stream指定字段分组排序

    统计

    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(39);
    list.add(10);
    list.add(78);
    list.add(39);
    
    //总和、平均值、最大值、最小值
    //针对int类型的数据进行累加操作,会使用参数lambda表达式将元素转换为int类型
    long sum = list.stream().collect(Collectors.summarizingInt(Integer::intValue)).getSum();
    System.out.println(sum);
    //平均值
    Double avg = list.stream().collect(Collectors.averagingInt(Integer::intValue));
    System.out.println(avg);
    //最大值
    Integer maxInteger = list.stream().collect(Collectors.maxBy(Integer::compare)).get();
    System.out.println(maxInteger);
    Integer min = list.stream().collect(Collectors.minBy(Integer::compareTo)).get();
    System.out.println(min);
    
    输出:
    176
    35.2
    78
    10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    也可以使用 mapToInt

    list.stream().mapToInt((x) -> x).summaryStatistics().getSum();

    findFirst

    findFirst返回非空集合中的第一个元素值,一般会与filter方法结合使用。

    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(39);
    list.add(10);
    list.add(78);
    Optional<Integer> op = list.stream().filter(i -> i > 20).findFirst();
    if (op.isPresent())
        System.out.println(op.get());
    else
        System.out.println("没有数据");
    
    输出:
    39
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    anyMatch

    anyMatch判定是否还有匹配的元素,返回一个boolean值。

    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(39);
    list.add(78);
    
    boolean res1 = list.parallelStream().anyMatch(i -> i > 39);
    boolean res2 = list.parallelStream().anyMatch(i -> i > 100);
    
    System.out.println(res1);
    System.out.println(res2);
    
    输出:
    true
    false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    判断所有的元素都满足条件或者没有元素满足条件返回true。这些方法用来检查整个流,所以可以通过使用并行流提高速度。

    reduce归并

    reduce方法用于将流中的元素进行进一步的归并计算。

    首先熟悉三个概念

    • Identity : 定义一个元素代表是归并操作的初始值,如果Stream 是空的,也是Stream 的默认结果。
    • Accumulator: 定义一个带两个参数的方法,第一个参数是上个归并方法的返回值,第二个是Strem 中下一个元素
    • Combiner: 调用一个方法来组合归并操作的结果,当归并是并行执行或者当累加器的方法和累加器的实现类型不匹配时才会调用此方法。
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    int result = numbers
      .stream()
      .reduce(0, (subtotal, element) -> subtotal + element);
      
    System.out.println(result);
    
    输出:
    21
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    reduce 方法的第一个参数 0 是 identity 初始值 ,此参数用来保存归并参数的初始值,当Stream 为空时也是默认的返回值。

    (subtotal, element) -> subtotal + element 是accumulator ,第一个参数是上次累计的和,第二个参数是数据流的下一个元素。

    为了使代码更简洁,我们可以用方法引用来代替 lambda 表达式。

    int result = numbers.stream().reduce(0, Integer::sum);
    
    • 1

    还可以操作一个 String 类型的数组,把数组的字符串进行拼接。

    List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
    String result = letters
      .stream()
      .reduce("", (partialString, element) -> partialString + element);
    
    • 1
    • 2
    • 3
    • 4

    简写:

    String result = letters.stream().reduce("", String::concat)
    
    • 1

    如果流中包含的是User 对象,但是累加方法的参数分别是数字和user 对象,而累加器的实现是求和:

    int result = users.stream()
      .reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(), Integer::sum);
    
    • 1
    • 2
    List<Integer> nums = Arrays.asList(13, 2, 2, 3, 7, 3, 5);
    //求和
    Integer sum = nums.stream().reduce((x,y)->x+y).get();
    System.out.println(sum);
    
    //简化写法
    Integer sum1 = nums.stream().reduce(Integer::sum).get();
    System.out.println(sum1);
    
    //可以指定初始值的求和计算,参数1就是累加器的初始值
    Integer sum2 = nums.stream().reduce(10, Integer::sum);
    System.out.println(sum2);
    
    //对元素的长度进行求和
    int sum3 = nums.stream().map(Object::toString).mapToInt(String::length).sum();
    System.out.println(sum3);
    
    输出:
    35
    35
    45
    8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    利用BigDecimal计算金额大数据

    实际项目中,一般金额都是用String类型进行存储,使用stream.sum()进行累加获取的值缺失精度,需要使用BigDecimal.add进行累加。

    List<String> nums = Arrays.asList("13.1", "2.1", "2.1", "3.1", "7.1");
    BigDecimal reduce = nums.stream().map(x -> new BigDecimal(x)).reduce(BigDecimal.ZERO, BigDecimal::add);
    System.out.println(reduce);
    
    输出:
    27.5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    遍历操作

    forEach

    forEach可以迭代流中的每个数据,forEach方法可以接收一个lambda表达式,并且在每个元素上执行该表达式。

    List<Integer> list = new ArrayList<>();
    list.add(10);
    list.add(39);
    list.add(78);
    
    list.stream().forEach(v -> {
        if (v > 30) {
            System.out.println(v);
        }
    });
    
    输出:
    39
    78
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    转换

    转换为数组

    Object[] arr = Stream.of(1, 2, 2, 5, 2, 7).toArray();
    System.out.println(Arrays.toString(arr));
    Integer[] arr2 = Stream.of(1, 2, 2, 5, 2, 7).toArray(Integer[]::new);
    System.out.println(Arrays.toString(arr2));
    
    • 1
    • 2
    • 3
    • 4

    转换为集合

    List<Integer> list = Stream.of(1, 2, 2, 5, 2, 7).collect(Collectors.toList()); 
    System.out.println(list); 
    
    ArrayList<Integer> list2 = Stream.of(1, 2, 3, 4,5).collect(Collectors.toCollection(ArrayList::new)); 
    System.out.println(list2); Set<Integer> set1 = Stream.of(1, 2, 2, 4, 2, 5).collect(Collectors.toSet()); 
    System.out.println(set1); 
    
    Stack<Integer> stack = Stream.of(1, 2, 2, 4, 2, 5).collect(Collectors.toCollection(Stack::new)); 
    System.out.println(stack);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    转换为String

    String res = Stream.of("1", "2", "2", "5", "2", "7").collect(Collectors.joining()); 
    System.out.println(res);
    
    • 1
    • 2

    练习题

    读取一个文本文件,查找最长一行内容的字符个数

    BufferedReader br = new BufferedReader(new FileReader("input/abc.logs")); 
    int maxLength=br.lines().mapToInt(String::length).max().getAsInt();
    br.close(); 
    System.out.println(maxLength);
    
    • 1
    • 2
    • 3
    • 4

    读取一个英文的文本文件,查找所有的英文单词,转换为全小写,并排序

    BufferedReader br = new BufferedReader(new FileReader("input/bbb.logs")); 
    List<String> words = br.lines().flatMap( line -> Stream.of(line.split(" "))).filter(word -> word.length() > 1) .map(String::toLowerCase).distinct().sorted().collect(Collectors.toList()); 
    br.close(); 
    System.out.println(words);
    
    • 1
    • 2
    • 3
    • 4

    计算列表对象某个字段值总和

    List<Product> list = new ArrayList<>();
    list.add(new Product("product1","11.2"));
    list.add(new Product("product2","22.3"));
    list.add(new Product("product3","33.4"));
    list.add(new Product("product4","11.2"));
    
    System.out.println("sum=" + list.stream().mapToDouble(v -> Double.valueOf(v.getPrice())).sum());
    System.out.println("sum=" + list.stream().map(v -> Double.valueOf(v.getPrice())).reduce(0.0, (x,y) -> x+y));
    System.out.println("sum=" + list.stream().map(v -> Double.valueOf(v.getPrice())).reduce(0.0, Double::sum));
    
    输出:
    sum=78.1
    sum=78.10000000000001
    sum=78.10000000000001
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    计算列表总和

    //如果不需要转换,就不需要使用map
    List<Integer> nums = Arrays.asList(13, 2, 2, 3, 7, 3, 5);
    System.out.println("sum=" + nums.stream().mapToInt(v->v).sum());
    System.out.println("sum=" + nums.stream().reduce(0, (x,y)->x+y));
    System.out.println("sum=" + nums.stream().reduce(0, Integer::sum));
    
    输出:
    sum=35
    sum=35
    sum=35
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    算法入门——冒泡排序、选择排序
    寻找链表相交结点问题
    English Learning - L3 Lesson4 VOA-Food 译文
    ElasticSearch from + size 分页查询过程分析,及其官方ES深度分页性能优化方法
    Spring Security+Spring Boot实现登录认证以及权限认证
    803_div2(Rising Sand, 接受军训!
    Origin中增加一列并更新绘图
    静息态fMRI中的非线性功能网络连接
    Redis的五种数据类型详解
    192:最近的系列思考2/犬岛APP 的使用理解
  • 原文地址:https://blog.csdn.net/weixin_40017062/article/details/126600145