java8 stream 流处理探索

2020-01-18  本文已影响0人  ShayHe

 使用java8也有一段时间了,但是一直没有整理过具体的笔记,现补充一下。
 java8流处理让集合操作变得简便了很多,通过我们在jdk7的时候,要进行很多行代码的编写才能完成的操作,使用流以后,可以在一行代码之间实现。
 我们先来准备一些基础数据,便于后面的操作。

/**
实体类
**/
package java8;

import lombok.AllArgsConstructor;

import lombok.Data;

@Data

@AllArgsConstructor

public class People {

private int id;

    private Stringname;

    private int age;

    private int score;

}

//main方法之前,先加载一些数据
static ListpeopleList =new ArrayList<>();

static ListsortList = Arrays.asList(1,3,8,6,7,9,10,5);

static {

People one =new People(1, "张三", 15, 88);

    People two =new People(2, "李四", 14, 99);

    People three =new People(3, "王五", 13, 100);

    People four =new People(4, "赵六", 12, 85);

    People five =new People(5, "王大麻子", 11, 55);

    People six =new People(6, "田七", 10, 53);

    People seven =new People(7, "小明", 14, 100);

    People eight =new People(8, "小红", 15, 75);

    People nine =new People(9, "小蓝", 17, 65);

    People ten =new People(10, "小绿", 13, 66);

    peopleList.add(one);

    peopleList.add(two);

    peopleList.add(three);

    peopleList.add(four);

    peopleList.add(five);

    peopleList.add(six);

    peopleList.add(seven);

    peopleList.add(eight);

    peopleList.add(nine);

    peopleList.add(ten);

}

好了,数据我们已经准备好了,那么就开始下一步吧。先从简单的开始说起。

  1. forEach
//最简单的遍历,就是把当前peopleList循环出来,并打印。之前我们最常用的就是for循环,现在一行就可以搞定了。
peopleList.stream().forEach(people -> System.out.println(people));

2.findAny, findFirst,从语意上可以看出来,两个方法都是查找。findAny就是查找符合条件的任意一条;findFirst查找符合条件的第一条。l因findFirst和findAny的使用方式,完全一致,就不再重复了。

        Optional<People> any = peopleList.stream().findAny();
        if(any.isPresent()){
           System.out.println(any.get());
        }
        //orElse和orElseGet这两个方法看起来差不多.区别在于:不论findAny返回的结果是不是空,orElse都会执行,而orElseGet则当findAny为空时才会执行。
        //只有当默认值已经事先定义的情况下,才使用orElse(),否则使用orElseGet()更好。
        People people = peopleList.stream().findAny().orElse(new People(12, "ls", 13, 66));
        System.out.println(people);
        People people1 = peopleList.stream().findAny().orElseGet(()->new People(11, "zs", 12, 22));
        System.out.println(people1);
        //获取不到则抛出异常,这个异常信息,根据业务需要来就可以了。
        People people2 = peopleList.stream().findAny().orElseThrow(RuntimeException::new);
        System.out.println(people2.toString());
  1. filter过滤,根据条件进行筛选数据。
        //filter 根据自己的需求过滤出自己所需要的内容,比如过滤出年龄大于15岁的people
        List<People> filterPeople = peopleList.stream().filter(people -> people.getAge() > 15).collect(Collectors.toList());
        filterPeople.stream().forEach(System.out::println);

4.map和flatMap,map是一维映射,也就是说,只有一层的List,使用其中的对象的某个属性是可以的。但是如果List嵌套,则只有第一层才能被映射。如果你想把两层List都映射出来。那么就请使用flatMap.

//map 抽取对象的某一个属性变成新的List,用名字形成一个新的List(一维,也就是说,只能使用一层List,如果List里面放一个List,此时map就只能把第一层List映射出来)
       List<String> peopleNamesList = peopleList.stream().map(People::getName).collect(Collectors.toList());
       peopleNamesList.stream().forEach(System.out::println);

//flatMap 跟map的区别就在于,flatMap可以把多层次的对象给映射出来
        List<List<Integer>> outer = new ArrayList<>();
        List<Integer> inner1 = new ArrayList<>();
        inner1.add(1);
        List<Integer> inner2 = new ArrayList<>();
        inner2.add(2);
        List<Integer> inner3 = new ArrayList<>();
        inner3.add(3);
        List<Integer> inner4 = new ArrayList<>();
        inner4.add(4);
        List<Integer> inner5 = new ArrayList<>();
        inner5.add(5);
        outer.add(inner1);
        outer.add(inner2);
        outer.add(inner3);
        outer.add(inner4);
        outer.add(inner5);
        List<Integer> result = outer.stream().flatMap(List::stream).collect(toList());
        System.out.println(result);

5.sorted 排序,分别使用了对象的属性排序,和Integer的排序。

//sorted 排序,默认是正序,可以指定对象的任意属性来进行排序。.reversed是反转的意思,正序的反转就相当于倒序排序。如果是基础数据类型,则可以直接使用sorted来排序,没有任何参数的时候,相当于传入Comparator.naturalOrder(),即正序排序。Comparator.reverseOrder(), 则是倒序排序。
       peopleList.stream().sorted(Comparator.comparing(People::getId).reversed()).forEach(System.out::println);
       sortList.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);

//写到这里,不得不提一下我之前犯下的一个错误。集合的sort()是非线程安全的。
for (int i = 0; i < 10; i++) {
            new Thread(() -> {
              peopleList.sort((o1,o2) -> o1.getScore()-o2.getScore());
            }).start();
        }
error.png
//而 stream 的 sorted是线程安全的。
for (int i = 0; i < 10; i++) {
            new Thread(() -> {
              peopleList.stream().sorted((o1,o2) -> o1.getScore()-o2.getScore()).forEach(System.out::println);
            }).start();
        }
下面这种写法就可以正常返回结果。
  1. collect 一般是一个stream操作的最后一步,把最终结果转成map或者list或者set。
//collect 一般是一个stream操作的最后一步,把最终结果转成map或者list或者set。
        //其中转成map的时候,有groupingBy和groupingByConcurrent(可以利用到多核架构的能力。groupingByConcurrent也有3个重载的方法,与groupingBy类似。但返回值必须是ConcurrentHashMap或其子类.)
         //转成list
        peopleList.stream().collect(Collectors.toList());
        //转成Map
        Map<Integer, List<People>> collect = peopleList.stream().collect(Collectors.groupingBy(people -> people.getAge()));
        collect.entrySet().stream().forEach(entry->{
            System.out.println(entry.getKey());
            System.out.println(entry.getValue().size());
        });
        //按照多个字段进行分组
        Map<Integer, Map<Integer, List<People>>> mutGroupMap = peopleList.stream().collect(Collectors.groupingBy(People::getAge, Collectors.groupingBy(People::getScore)));
        mutGroupMap.entrySet().stream().forEach(entry->{
            System.out.println(entry.getKey());
            System.out.println(entry.getValue().size());
        });
//注意,当我们的集合当中有重复数据的时候,groupingby会把结果改为数组:
peopleList.add(new People(10,"小绿", 13,66));
        Map<String, Map<Integer, List<People>>> flatMap = peopleList.stream().collect(Collectors.groupingBy(People::getName, Collectors.groupingBy(People::getScore)));
        flatMap.entrySet().stream().forEach(entry->{
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        });
1.png
groupingby 优点:非常适合分组归类使用,方便快捷。
                   缺点也很明显,当分组的条件多的时候,这个Map就难以阅读,维护了。
groupingby不支持null键

//toMap 速度快,支持一个null键
//添加一个重复数据
        peopleList.add(new People(10,"小绿", 13,66));
//报异常
        Map<String, Integer> toMap2 = peopleList.stream().collect(Collectors.toMap(People::getName, People::getAge));
        toMap2.entrySet().stream().forEach(entry -> {
            System.out.println(entry.getKey() + "--" + entry.getValue());
        });
   //Exception in thread "main" java.lang.IllegalStateException: Duplicate key 13
    //at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
    //at java.util.HashMap.merge(HashMap.java:1254)
    //at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
    //at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    //at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
    //at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    //at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
    //at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    //at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    //at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    //at com.hexy.java8.GroupingByAndToMap.main(GroupingByAndToMap.java:43)
        Map<String, Integer> toMap = peopleList.stream().collect(Collectors.toMap(People::getName, People::getAge ,(oldValue, newValue) -> oldValue , HashMap::new));
        toMap.entrySet().stream().forEach(entry -> {
            System.out.println(entry.getKey() + "--" + entry.getValue());
        });
    //正常打印,我们来看一下,如果我们不写第三个参数,那jdk默认帮我们做了什么吧?
   
1.png 2.png

7.max min 根据对象的某个属性取最大值或者最小值

//max min 根据对象的某个属性取最大值或者最小值,比如取分数最高的,和分数最低的 具体的对比规则,可以直接使用Comparator传入
       Optional<People> maxScore = peopleList.stream().max(Comparator.comparing(People::getScore));
       Optional<People> minScore = peopleList.stream().min(Comparator.comparing(People::getScore));
       System.out.println(maxScore.get());
       System.out.println(minScore.get());
  1. limit 这个跟sql的limit很类似。只取前N个。
//limit 限制取 limit个 成绩大于90的前两个(和一个例子,只是找出来了成绩大于90分的人,但是并没有数量上的限制,所以找出来3人,但是第二个,由于写了limit(2),则就是限制,我只取两条)
        List<People> greatThanNinetyList = peopleList.stream().filter(people -> people.getScore() > 90).collect(Collectors.toList());
        System.out.println(greatThanNinetyList.size());
        List<People> greatThanNinetyLimitTwoList = peopleList.stream().filter(people -> people.getScore() > 90).limit(2).collect(Collectors.toList());
        System.out.println(greatThanNinetyLimitTwoList.size());

9.count 集合的数量

System.out.println(peopleList.stream().count());

10.distinct 这个跟sql的distinct也很类似

//去重, 我们再加入一个小绿 10, "小绿", 13, 66
        peopleList.add(new People(10, "小绿", 13, 66));
        //应该有11个
        System.out.println("distinct前的数量:" + peopleList.size());
       //有10个
        System.out.println("distinct后的数量:" + peopleList.stream().distinct().collect(Collectors.toList()).size());

11.allMatch(全部符合设置的条件) anyMatch(任意一个符合设置的条件) noneMatch(没有一个符合设置的条件)返回值为boolean

// allMatch(全部符合设置的条件) anyMatch(任意一个符合设置的条件) noneMatch(没有一个符合设置的条件)
        boolean allMatch = peopleList.stream().allMatch(people -> people.getScore()>99);
        System.out.println(allMatch);

        boolean anyMatch = peopleList.stream().anyMatch(people -> people.getScore()>99);
        System.out.println(anyMatch);

        boolean noneMatch = peopleList.stream().anyMatch(people -> people.getScore()>100);
        System.out.println(noneMatch);

12.skip 跳过指定个数的元素

//只打印出peopleList的后7个元素
peopleList.stream().skip(3).forEach(System.out::println);

13.peek 是一个中间操作,它一般用于调试,或者你需要修改stream的元素的时候,使用它比较合适。因为它改变了数据之后,并不会消耗掉stream,你可以继续的执行后面的操作。

peopleList.stream().peek(people -> {
            if(people.getScore() == 100){
                people.setScore(1000);
            }
        }).forEach(System.out::println);

13.foreachOrdered和unordered 在并发情况下,forechOrdered是必须按照集合的顺序来循环。而unordered则是在并发条件下,取消必须按集合顺序循环。

//foreachOrdered 严格按照list的顺序输出(一般只有并行的时候用)
        peopleList.stream().parallel().forEach(System.out::println);
        System.out.println("----------------------------");
        // 取消严格按照顺序循环,有利于执行效率的提高
        peopleList.stream().unordered().parallel().forEach(System.out::println);
  1. reduce
//reduce 1.第一个重载函数的入参是一个BinaryOperator的函数式接口(该函数式接口接收两个T类型的参数并返回T类型)。该方法返回的是一个Optional类型的值。
        sortList.stream().reduce((item, next)-> item + next).ifPresent(System.out::println);
        //reduce 2.
        Integer reduce = sortList.stream().reduce(0, (item, next) -> item + next);
        System.out.println(reduce);
        //reduce 3.
        Integer reduce1 = sortList.stream().reduce(0, (item, next) -> item + next, (item, next) -> item);
        System.out.println(reduce1);

15.parallel 开启并行操作

//parallel 把当前流设置为并行,isParallel是检测当前Stream是否是并行。
        //false 因为没有开启并行
        System.out.println(peopleList.stream().isParallel());
        //true 因为手动开启了并行
        System.out.println(peopleList.stream().parallel().isParallel());

16.sequential 返回串行的流,也就是取消并行

peopleList.stream().sequential();

17.Spliterator是一个可分割迭代器(splitable iterator),对于并行处理的能力大大增强,Spliterator就是为了并行遍历元素而设计的一个迭代器,jdk1.8中的集合框架中的数据结构都默认实现了spliterator。

Spliterator<People> mySpliterator = peopleList.stream().spliterator();
        AtomicInteger count = new AtomicInteger(0);
        for(int i=0;i<4;i++){
            new Thread(()->{
                String threadName = Thread.currentThread().getName();
                System.out.println("线程"+threadName+"开始运行-----");
                mySpliterator.trySplit().forEachRemaining(people -> {
                    if(people.getScore()>80){
                        int num = people.getScore();
                        count.addAndGet(num);
                        System.out.println("数值:"+num+"------"+threadName);
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                System.out.println("线程"+threadName+"运行结束-----");
            }).start();
        }
//得到的结果
线程Thread-2开始运行-----
线程Thread-0开始运行-----
数值:100------Thread-0
数值:88------Thread-2
线程Thread-3开始运行-----
线程Thread-1开始运行-----
线程Thread-1运行结束-----
线程Thread-3运行结束-----
线程Thread-0运行结束-----
数值:99------Thread-2
数值:100------Thread-2
数值:85------Thread-2
线程Thread-2运行结束-----
上一篇 下一篇

猜你喜欢

热点阅读