java Stream类的 超全API及实战总结

  1. 不知不觉java 8已经是个很多年前的版本了,但是他真的很强大,有其是Stream相关类,用好了用习惯了真的很难再去使用for 循环配合其他逻辑去写一些繁琐的方法。我用了很久的Stream类,其中有很多因为使用而发现的API,今天做了个整理,分享给大家。
  2. Stream类主要是对集合进行各种快捷的处理,他是流式,或者说是管道式的处理,与我们平常的直接循环调用各种API而言他的API速度要更快。
  3. Stream可以认为是一步一步的,链式调用的,有步骤的API,从流程上可以分为:创建流、数据处理、数据收集三种类型。
  4. 其中数据处理的API最多,大致可以分为以下几种操作类型:筛选与切片、映射与排序、查找与匹配、规约与收集。
  5. Stream主要是对集合类,即Collection类的处理为主。接下来开始结合代码讲解,本文只讲解常用的,过于生僻的或者差不多的笔者在此不多赘述。
  6. 首先是第一部分,创建流。
		String[] str = {"5", "2", "3", "4", "1"};
        List<String> strList = Arrays.asList(str);
        // stream()与parallelStream()是 Collection(集合)接口的方法因此操作的集合类需要实现Collection接口才能调用
        // stream默认是串行流,也就是单线程的去处理流的后续逻辑;
        Stream<String> stream1 = strList.stream();
        // parallelStream创建的是并行流,即并行去处理流中的数据,建议慎用,容易出现并发不安全的问题。
        Stream<String> stream2 = strList.parallelStream();
        // stream.of 通常用来将数组转换成流
        Stream<List<String>> stream3 = Stream.of(strList);
  1. 通过以上方法就可以获取到一个新的stream对象了,接下来则是数据处理部分的API。
 private static List<StudentModel> list= Arrays.asList(
            new StudentModel("1","张三",11,"男"),
            new StudentModel("2","李四",22,"女"),
            new StudentModel("3","王五",21,"男"),
            new StudentModel("4","赵六",17,"女"),
            new StudentModel("4","赵六",17,"女"),
            new StudentModel("4","赵六",17,"女")
    );

    /**stream的筛选与切片
     * 1、filter()接收lambda,从流中排出指定的元素
     * 2、limit()截断流,使其元素不超过给定数量,打到给定数量时后续不再运行
     * 3、skip()跳过元素,返回一个扔掉了前n个元素的流;若流中元素不足N个,则返回一个空流
     * 4、distinct()去重,通过流所生成元素的hashCode()和equals()去重;
     *   去重在对自己写的对象使用时要自己重写hashCode和equals方法
    */
    private static void test3(){
        Stream<StudentModel> stream = list.stream().filter(a -> a.getAge() > 15);
        //过滤;这种写法直接把过程和启用写在了一起
        list.stream().filter(a->a.getAge()>15).forEach(param-> System.out.println(param));
        System.out.println("over-filter");
        //只取前两个,如果一边过滤一边截取在满足截取后将不在进行操作
        list.stream().filter(a->a.getAge()>15).limit(2).forEach(param-> System.out.println(param));
        System.out.println("over-limit");
        //跳过前两个满足条件的再进行操作
        list.stream().skip(2).forEach(param-> System.out.println(param));
        System.out.println("over-skip");
        list.stream().distinct().forEach(param-> System.out.println(param));
    }
 private static List<StudentModel> list= Arrays.asList(
            new StudentModel("1","张三",11,"男"),
            new StudentModel("2","李四",22,"女"),
            new StudentModel("3","王五",21,"男"),
            new StudentModel("4","赵六",17,"女"),
            new StudentModel("4","赵其",17,"女"),
            new StudentModel("4","赵芭",17,"女")
    );

    /**
     * 映射
     * map---接收Lambda,将元素转化成其他形式或提取信息;
     * 接收一个函数作为参数,该函数会被应用在每个元素上,并被其映射成一个新的元素。
     * flatMap----基本与map相同,不过map会返回一个新的stream将返回的数据包在里面,而flatMap是将返回的stream直接返回
     */
    public static void test1(){
        List<String> stringList = Arrays.asList("aa", "bb", "cc", "dd");
        //元素全转为大写
        stringList.stream().map(str->str.toUpperCase()).forEach(str-> System.out.println(str));
        //map返回的是一个新的流,将原先的Stream包装在新的stream中了
        Stream<Stream<Character>> streamStream = stringList.stream().map(ThirdTest::filterCharacter);
        //flatMap是直接返回返回的流
        Stream<Character> characterStream = stringList.stream().flatMap(ThirdTest::filterCharacter);
        //将对象中的名字全都提取出来
        list.stream().map(model->model.getName()).forEach(name-> System.out.println(name));
        Stream<String> newStrList = stringList.stream().flatMap(str -> Stream.of(str.toUpperCase()));
        newStrList.forEach(str-> System.out.println(str));
    }
private static List<StudentModel> list= Arrays.asList(
            new StudentModel("1","张三",11,"男", StudentModel.Status.FREE),
            new StudentModel("2","李四",22,"女", StudentModel.Status.BUSY),
            new StudentModel("3","王五",21,"男", StudentModel.Status.MOYU),
            new StudentModel("4","赵六",17,"女",StudentModel.Status.FREE),
            new StudentModel("4","赵其",17,"女",StudentModel.Status.BUSY),
            new StudentModel("4","赵其",17,"女",StudentModel.Status.BUSY),
            new StudentModel("4","赵芭",17,"女",StudentModel.Status.FREE)
    );

    //查找与匹配
    private static void test1(){
        //allMatch--检查是否匹配所有元素
        boolean a = list.stream().allMatch(student -> student.getStatus().equals(StudentModel.Status.BUSY));
        System.out.println(a+"--a");
        //anyMatch--检查是否匹配至少一个元素
        boolean b =list.stream().anyMatch(student -> student.getStatus().equals(StudentModel.Status.BUSY));
        System.out.println(b+"--b");
        //noneMatch--检查是否没有匹配所有元素,就是有的符合这个条件
        boolean c =list.stream().noneMatch(student -> student.getStatus().equals(StudentModel.Status.BUSY));
        System.out.println(c+"--c");
        //findFirst--返回第一个元素,Optional是1.8中新增的一个容器,目前已知是可以避免对象为空从而造成的空指针
        Optional<StudentModel> first = list.stream().findFirst();
        System.out.println(first+"---first");
        //findAny--返回当前刘中的任意元素
        //因为stream是串行的的所以是按顺序的返回第一个
        Optional<StudentModel> any = list.stream().findAny();
        System.out.println(any+"---any");
        //因为parallelStream是并行的的所以是随机返回的
        Optional<StudentModel> any1 = list.parallelStream().findAny();
        //get()是Optional容器获取容器中对象的方法
        System.out.println(any1.get()+"---any1");
        //count--返回流中元素总个数
        long count = list.stream().count();
        System.out.println(count+"--count");
        //max--返回流中指定规则的最大值
        Optional<StudentModel> max = list.stream().max((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()));
        System.out.println(max.get()+"--max");
        //min--返回流中制定规则的最小值
        Optional<StudentModel> min = list.stream().min((e1, e2) -> Integer.compare(e1.getAge(),e2.getAge()));
        System.out.println(min.get()+"--min");
        //简化的写法,直接获取的最低工资
        Optional<Integer> minSimple = list.stream().map(student -> student.getAge()).min(Integer::compareTo);
        System.out.println(minSimple.get()+"--minSimple");
    }
  1. 接下来则是数据收集部分的API了,数据收集可以概括为将处理完的数据存为一个新的对象或者新的集合。
private static List<StudentModel> list= Arrays.asList(
            new StudentModel("1","张三",11,"男", StudentModel.Status.FREE),
            new StudentModel("2","李四",22,"女", StudentModel.Status.BUSY),
            new StudentModel("3","王五",21,"男", StudentModel.Status.MOYU),
            new StudentModel("4","赵六",17,"女",StudentModel.Status.FREE),
            new StudentModel("4","赵其",17,"女",StudentModel.Status.BUSY),
            new StudentModel("4","赵其",17,"女",StudentModel.Status.BUSY),
            new StudentModel("4","赵芭",17,"女",StudentModel.Status.FREE)
    );
    
    //reduce--规约,将流中的元素反复结合(运算)起来得到一个值
    private static void test2(){
        List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
        //reduce共有三种,T reduce(T identity, BinaryOperator accumulator);
        //0是初始值用来第一次作为X的值进行运算,此后累加。
        Integer reduceInteger = integerList.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduceInteger+"--reduceInteger");
        //map与reduce的联结使用也称为map-reduce模式,大数据中广泛应用;map进行提取,reduce进行规约的操作
        Integer reduceModel = list.stream().map(studentModel -> studentModel.getAge()).reduce(0, (x, y) -> x + y);
        System.out.println(reduceModel+"--reduceModel");
        //另一种写法
        Optional<Integer> reduceModelInOptional = list.stream().map(StudentModel::getAge).reduce(Integer::sum);
        System.out.println(reduceModelInOptional.get()+"--reduceModelInOptional");
    }

    //collect,收集,将流转化为其他形式,接收一个Collector接口的实现
    private static void test3(){
        Stream<String> nameStream = list.stream().map(studentModel -> studentModel.getName());
        //Collectors是一个工具类包含常用的流转换的静态方法
        List<String> nameList = nameStream.collect(Collectors.toList());
        nameList.forEach(name-> System.out.println(name));
        System.out.println("nameList-------------------------");
        Stream<String> nameStreamSecond = list.stream().map(studentModel -> studentModel.getName());
        Set<String> nameSet = nameStreamSecond.collect(Collectors.toSet());
        nameSet.forEach(name-> System.out.println(name));
        System.out.println("nameSet-------------------------");

    }
  1. 以上是我在学习 stream时学到的API,接下来放上一些我自己在实际开发中发现的一些小技巧及特殊的API吧。
// stream因为是流式操作中不能进行数据的改变,要使用AtomicReference包裹保证数据的一致性
//计算总库存
AtomicReference<Integer> inventory= new AtomicReference<>(0);
list.forEach(obj-> 
      inventory.updateAndGet(v -> v + obj.getInventory()));
vo.setInventory(inventory.get());

// Collectors.summingInt()、Collectors.summarizingLong()、Collectors.summarizingDouble() 这三个分别用于int、long、double类型数据一个求总操作,返回的是一个SummaryStatistics(求总),包含了数量统计count、求和sum、最小值min、平均值average、最大值max。虽然IntStream、DoubleStream、LongStream 都可以是求和sum 但是也仅仅只是求和,没有summing结果丰富。如果要一次性统计、求平均值什么的,summing还是非常方便的。
int inventory = list.stream().
      mapToInt(GoodsDetailSkuRel::getInventory).sum();
vo.setInventory(inventory);

// filter里面可以做多条件过滤
List<Object> collect= list.stream().
        filter(item-> item.getIsSeckill()!=null && item.getIsSeckill().equals(0))
        .collect(Collectors.toList());

// 某个字段的快速计算
map.get(user.getId()).stream().
    mapToLong(param->param.getRipeBeansNum()).sum();

// 在需要打断stream 的for循环时可以考虑使用 anyMatch 代替for循环

// peek用法与forEach相同,不过会返回新的流可以进行后续继续的操作。
stream().peek(item->{
});

// 可以在 Collectors.groupingBy方法里对想要的key进行在进一步的处理
Map<String, List<T>> collect = list.stream().collect(Collectors.groupingBy(
	item -> String.join(StrUtil.DASHED, this.getPropertiesValueStrs(item, properties))
        , Collectors.toList())
); 

// 可以在 Collectors.groupingBy方法 不仅对key进行处理,还可以对键的数据也在进一步的处理,避免了收集之后再一次多余的数据处理。
Map<String, List<String>> businessLineMap = channelInfoDtos.stream()
.filter(item -> channelInfoIds.contains(item.getChannelId()))
.collect(Collectors.groupingBy(ChannelInfoDto::getBusinessLine, 
    Collectors.mapping(ChannelInfoDto::getChannelName, Collectors.toList())));      
  1. 以上记载了笔者认为最为常用的API,希望能帮到大家。

留言:码字不易,大佬们觉得文章有帮助,动动小手点个赞支持一下,蟹蟹!

你可能感兴趣的:(java,spring,boot)