美文网首页
10. JDK8的Stream流

10. JDK8的Stream流

作者: 轻轻敲醒沉睡的心灵 | 来源:发表于2024-04-21 15:22 被阅读0次

    Jdk8中新增了Lambda表达式和Stream流,而Stream流的操作是在Lambda表达式的基础上进行的。也就是要想流畅的使用Stream流,你得先会点Lambda表达式。

    1. Stream流的使用流程:

    Stream使用流程
    需要注意的3点:
      1. Stream是基于数据源的对象,要先有数据源才能有流,不能凭空new一个出来;
      1. Stream流的操作不会修改数据源的数据,流的一切操作都是针对生成的流;
      1. Stream流操作一次后就会失效,就像迭代器一样,想要再次使用必须重新生成一个新的Stream。

    2. 获取流

    先说一下什么样的数据用Stream处理。生产上用的最多的应该是List<T>集合数据了,可以是int、String的集合,也可以是实体类对象的集合。除此,还有数组和Map都可以用Stream流处理。

    • List集合
    List<User>  userList = new ArrayList<>();
    Long count = userList.stream().collect(Collectors.counting());  
    
    • 数组
    int[] arr = {1,2,3,4,5,6,7,8};
    Arrays.stream(arr).forEach(i-> System.out.println(i));
    String[] strs = {"a","b","c"};
    Arrays.stream(strs).forEach(i-> System.out.println(i));
    
    • Map
    Map<Integer, List<String>> map = new HashMap<>();
    // 1. key的流
    map.keySet().stream().forEach(i-> System.out.println(i));
    // 2. map的流
    map.entrySet().stream().forEach(i-> System.out.println(i));
    

    3 Stream流的常用方法

    3.1 中间方法

      1. filter()
        过滤出符合条件的元素
    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    list.add("4");
    list.add("5");
    list.add("6");
    list.add("7");
    list.add("8");
    list.add("9");
    list.add("0");
    List<String> list1 = list.stream().filter(i -> (Integer.valueOf(i)) % 3 == 0).collect(Collectors.toList());
    list1.forEach(System.out::println);
    
    filter结果
      1. limit()

    截取前面几个

    List<String> list2 = list.stream().limit(5).collect(Collectors.toList());
    list2.forEach(System.out::println);
    
    limit结果
      1. skip()

    跳过前几个元素

    List<String> list3 = list.stream().skip(5).collect(Collectors.toList());
    list3.forEach(System.out::println);
    
    skip结果
      1. distinct()

    去重

    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    list.add("4");
    list.add("2");
    list.add("3");
    list.forEach(System.out::println);
    System.out.println("=========");
    List<String> list4 = list.stream().distinct().collect(Collectors.toList());
    list4.forEach(System.out::println);
    
    distinct结果
      1. map()

    实现流中元素类型的转换。用的比较多有2种情况:

    1. 获取对象流中某个字段组成集合或计算值
    List<String> nameList = empList.stream().map(employe-> employe.getName()).collect(Collectors.toList());
    BigDecimal sum = empList.stream().map(employe-> employe.getSalary()).reduce(BigDecimal.ZERO, BigDecimal::add);
    
    1. 由一个对象流,拿到另一个对象流
    List<Employe> empList = userList.stream().map(i -> {
                            Employe emp = new Employe();
                            emp.setName(i.getName());
                            emp.setAge(i.getAge());
                            return emp;
                        }).collect(Collectors.toList());
    
      1. sorted()

    排序。这个是比较灵活的,可以直接默认自然排序,也可以重写比较方法,包括多个字段的比较

    1. 自然排序
    // 升序
    List<String> list5 = list.stream().sorted().collect(Collectors.toList());
    list5.forEach(System.out::println);
    // 降序
    List<String> list6 = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
    list6.forEach(System.out::println);
    
    sorted升序结果
    1. 重写排序,一般是对象指定字段排序或多字段排序
    List<User> userList = new ArrayList<>();
    User u1 = new User(15, "张三1");
    User u2 = new User(25, "张三2");
    User u3 = new User(16, "李四1");
    User u4 = new User(26, "李四2");
    User u5 = new User(17, "王五1");
    User u6 = new User(27, "王五2");
    userList.add(u1);
    userList.add(u2);
    userList.add(u3);
    userList.add(u4);
    userList.add(u5);
    userList.add(u6);
    // 指定字段,默认升序
    List<User> list7 = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
    // 指定字段降序
    List<User> list8 = userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
    // 指定字段降序
    List<User> list9 = userList.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder())).collect(Collectors.toList());
    // 2个字段升序
    List<User> list10 = userList.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(Collectors.toList());
    // 一降一升1
    List<User> list11 = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getName)).collect(Collectors.toList());
    // 一降一升2
    List<User> list12 = userList.stream().sorted(Comparator.comparing(User::getAge, Comparator.reverseOrder()).thenComparing(User::getName)).collect(Collectors.toList());
    // 双降,双降也可以第2种写法,这里不写了
    List<User> list13 = userList.stream().sorted(Comparator.comparing(User::getAge).reversed().thenComparing(User::getName).reversed()).collect(Collectors.toList());
    

    注意,以上8和9的不同在于:8是用默认的比较器升序排序,然后结果做一个反转变降序;而9直接是用降序比较器做的排序,直接就是降序的结果。
    多个字段,就按2个字段的写法就行。

      1. flatMap()

    处理复杂的数据结构。举个栗子:有一订单列表List<order> orderList,而每一个订单又包含商品列表List<goods> goodsList,每个商品都有自己的价格price、类别category等属性。要想拿到订单列表中所有商品的流,可以用flatMap()。

    // 1. 某个商品一共卖了几件
    List<goods> goodsList = orderList.stream().flatMap(order -> order.getGoodsList().stream()).filter(goods -> "12345".equas(goods.getId())).collect(Collectors.toList());
    

    需要注意:flatMap中需要的是一个流对象。

    3.2 结束方法

      1. toArray()

    意思很明显,就是转为数组。

    Object[] arr = list.stream().toArray();
    String[] arr1 = list.stream().toArray(i -> new String[i]);
    System.out.println(Arrays.toString(arr));
    System.out.println(Arrays.toString(arr1));
    
    toArray结果

    其实这个吧,一般也不会用,一般的转 数组用自己的api就行。

    String[] arr2 = list.toArray(new String[list.size()]);
    System.out.println(Arrays.toString(arr2));
    
      1. count()

    计数统计用

    long count = list.stream().count();
    System.out.println(count);
    
      1. max()/min()

    找最大/最小的

    Optional<User> max = userList.stream().max((i1, i2) -> Integer.compare(i1.getAge(), i2.getAge()));
    System.out.println(max.get());
    Optional<User> min = userList.stream().min((i1, i2) -> i1.getAge() - i2.getAge());
    System.out.println(min.get());
    
    max/min结果
      1. sum()/average()

    简单的求和/平均。要mapTo配合

    // 求和
    int sum = userList.stream().mapToInt(i -> i.getAge()).sum();
    double sum2 = userList.stream().mapToDouble(i -> i.getAge()).sum();
    long sum3 = userList.stream().mapToLong(i -> i.getAge()).sum();
    // 平均
    OptionalDouble sum = userList.stream().mapToInt(i -> i.getAge()).average();
    OptionalDouble sum2 = userList.stream().mapToDouble(i -> i.getAge()).average();
    
      1. anyMatch()/allMatch()

    条件判断。这个感觉就是为了简化代码用的。
    比如,看看有没有16岁的学生:

    boolean b = userList.stream().anyMatch(i -> i.getAge() == 16);
    System.out.println(b);
    
      1. collect()

    收集。收集成指定类型的数据。
    可以灵活的使用自带的api。除了上面那些数量、求和、平均值、最小值、最大值,其他复杂的也可以

    1. 上面那些已经有的,简单点的
    //  数量
    long count = userList.stream().collect(Collectors.counting());
    // 求和
    Integer si = userList.stream().collect(Collectors.summingInt(i -> i.getAge()));
    //  平均
    Double ad = userList.stream().collect(Collectors.averagingDouble(i -> i.getAge()));
    // 最大
    Optional<User> o = userList.stream().collect(Collectors.maxBy((i1, i2) -> i1.getAge() - i2.getAge()));
    // 最小
    Optional<User> o1 = userList.stream().collect(Collectors.minBy((i1, i2) -> i1.getAge() - i2.getAge()));
    // 数量、求和、平均值、最小值、最大值
    DoubleSummaryStatistics dss = userList.stream().collect(Collectors.summarizingDouble(i -> i.getAge()));
    
    summarizingDouble结果
    这里需要注意一下:对于BigDecimal的处理使用的另外的方法
    itemList.stream().map(i -> i.getMoney()).reduce(BigDecimal.ZERO, BigDecimal::add);
    
    1. 复杂点的
    // 收集成list、set、map
    List<User> collect = userList.stream().collect(Collectors.toList());
    Set<User> set = userList.stream().collect(Collectors.toSet());
    Map<String, Integer> map = userList.stream().collect(Collectors.toMap(User::getName, User::getAge));
    // 拼接字段成字符串
    String nameString = userList.stream().map(i -> i.getName()).collect(Collectors.joining(","));
    // 分组,注意分组后的对象格式
    Map<Integer, List<User>> m1= userList.stream().collect(Collectors.groupingBy(User::getAge));
    // 2个字段分组,注意分组后的对象格式
    Map<Integer, Map<String, List<User>>> m2= userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getName)));
    // 分组统计,注意分组后的对象格式
    Map<Integer, Long> m3 = userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.counting()));
    
      1. reduce()

    规约。啥是规约,就是把列表流中元素的个数 逐渐减少,最后出的结果是和流中的元素一个类型的。
    reduce是很灵活的,所以也是很难的搞的,除非特殊需求,一般用上面的api就能解决。
    需要注意一点:你想要通过reduce得到一个什么类型的结果,传给reduce的流中元素必须使用hi这个类型。
    reduce 的实现,有3个重新的方法,分别是 1个参数,2个参数,3个参数

    reduce的3个实现方法
    • 1个参数的这个BinaryOperator<P_OUT> accumulator是规约的核心,最后的结果是根据你这个算法来算的,也是最难得。
    • 2个参数的就是多加了个初始值identity,当流中没元素的时候,返回这个初始值,相当于1个参数的完善(注意,不为空时,最后的结果是把初始值算进去的,用求和的例子一眼看出)。需要注意的是1个参数和2个参数返回类型不一样。因为1个参数没考虑空的情况。
    Optional<Integer> op = userList.stream().map(i -> i.getAge()).reduce((i1, i2) -> i1 + i2);
    Integer sum = userList.stream().map(i -> i.getAge()).reduce(2, (i1, i2) -> i1 + i2);
    
    • 3个参数的BinaryOperator<R> combiner是考虑了并行流的情况,是把第2个参数并行的结果用第3个参数处理,一般很少用了,并行就要考虑线程安全问题了。用的时候很容易出错。
    List<Integer> asList = Arrays.asList(1, 2, 3);
    Integer reduce = asList.stream().reduce(4, (i1, i2) -> i1 + i2);
    System.out.println(reduce);
    Integer reduce1 = asList.stream().parallel().reduce(4, (i1, i2) -> i1 + i2);
    System.out.println(reduce1);
    Integer reduce2 = asList.stream().parallel().reduce(1, (i1, i2) -> i1 + i2, (a, b) -> a * b);
    System.out.println(reduce2);
    
    reduce结果

    需要注意:并行流别乱用,尤其reduce中,别给自己挖坑。

    相关文章

      网友评论

          本文标题:10. JDK8的Stream流

          本文链接:https://www.haomeiwen.com/subject/kwnexjtx.html