java8 stream 流處理探索

?使用java8也有一段時間了,但是一直沒有整理過具體的筆記,現(xiàn)補充一下。
?java8流處理讓集合操作變得簡便了很多,通過我們在jdk7的時候,要進行很多行代碼的編寫才能完成的操作,使用流以后,可以在一行代碼之間實現(xiàn)。
?我們先來準備一些基礎(chǔ)數(shù)據(jù),便于后面的操作。

/**
實體類
**/
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方法之前,先加載一些數(shù)據(jù)
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);

}

好了,數(shù)據(jù)我們已經(jīng)準備好了,那么就開始下一步吧。先從簡單的開始說起。

  1. forEach
//最簡單的遍歷,就是把當前peopleList循環(huán)出來,并打印。之前我們最常用的就是for循環(huán),現(xiàn)在一行就可以搞定了。
peopleList.stream().forEach(people -> System.out.println(people));

2.findAny, findFirst,從語意上可以看出來,兩個方法都是查找。findAny就是查找符合條件的任意一條;findFirst查找符合條件的第一條。l因findFirst和findAny的使用方式,完全一致,就不再重復(fù)了。

        Optional<People> any = peopleList.stream().findAny();
        if(any.isPresent()){
           System.out.println(any.get());
        }
        //orElse和orElseGet這兩個方法看起來差不多.區(qū)別在于:不論findAny返回的結(jié)果是不是空,orElse都會執(zhí)行,而orElseGet則當findAny為空時才會執(zhí)行。
        //只有當默認值已經(jīng)事先定義的情況下,才使用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);
        //獲取不到則拋出異常,這個異常信息,根據(jù)業(yè)務(wù)需要來就可以了。
        People people2 = peopleList.stream().findAny().orElseThrow(RuntimeException::new);
        System.out.println(people2.toString());
  1. filter過濾,根據(jù)條件進行篩選數(shù)據(jù)。
        //filter 根據(jù)自己的需求過濾出自己所需要的內(nèi)容,比如過濾出年齡大于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的區(qū)別就在于,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是反轉(zhuǎn)的意思,正序的反轉(zhuǎn)就相當于倒序排序。如果是基礎(chǔ)數(shù)據(jù)類型,則可以直接使用sorted來排序,沒有任何參數(shù)的時候,相當于傳入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();
        }
下面這種寫法就可以正常返回結(jié)果。
  1. collect 一般是一個stream操作的最后一步,把最終結(jié)果轉(zhuǎn)成map或者list或者set。
//collect 一般是一個stream操作的最后一步,把最終結(jié)果轉(zhuǎn)成map或者list或者set。
        //其中轉(zhuǎn)成map的時候,有g(shù)roupingBy和groupingByConcurrent(可以利用到多核架構(gòu)的能力。groupingByConcurrent也有3個重載的方法,與groupingBy類似。但返回值必須是ConcurrentHashMap或其子類.)
         //轉(zhuǎn)成list
        peopleList.stream().collect(Collectors.toList());
        //轉(zhuǎn)成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());
        });
//注意,當我們的集合當中有重復(fù)數(shù)據(jù)的時候,groupingby會把結(jié)果改為數(shù)組:
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 優(yōu)點:非常適合分組歸類使用,方便快捷。
                   缺點也很明顯,當分組的條件多的時候,這個Map就難以閱讀,維護了。
groupingby不支持null鍵

//toMap 速度快,支持一個null鍵
//添加一個重復(fù)數(shù)據(jù)
        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());
        });
    //正常打印,我們來看一下,如果我們不寫第三個參數(shù),那jdk默認幫我們做了什么吧?
   
1.png
2.png

7.max min 根據(jù)對象的某個屬性取最大值或者最小值

//max min 根據(jù)對象的某個屬性取最大值或者最小值,比如取分數(shù)最高的,和分數(shù)最低的 具體的對比規(guī)則,可以直接使用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分的人,但是并沒有數(shù)量上的限制,所以找出來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 集合的數(shù)量

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

10.distinct 這個跟sql的distinct也很類似

//去重, 我們再加入一個小綠 10, "小綠", 13, 66
        peopleList.add(new People(10, "小綠", 13, 66));
        //應(yīng)該有11個
        System.out.println("distinct前的數(shù)量:" + peopleList.size());
       //有10個
        System.out.println("distinct后的數(shù)量:" + peopleList.stream().distinct().collect(Collectors.toList()).size());

11.allMatch(全部符合設(shè)置的條件) anyMatch(任意一個符合設(shè)置的條件) noneMatch(沒有一個符合設(shè)置的條件)返回值為boolean

// allMatch(全部符合設(shè)置的條件) anyMatch(任意一個符合設(shè)置的條件) noneMatch(沒有一個符合設(shè)置的條件)
        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 跳過指定個數(shù)的元素

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

13.peek 是一個中間操作,它一般用于調(diào)試,或者你需要修改stream的元素的時候,使用它比較合適。因為它改變了數(shù)據(jù)之后,并不會消耗掉stream,你可以繼續(xù)的執(zhí)行后面的操作。

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

13.foreachOrdered和unordered 在并發(fā)情況下,forechOrdered是必須按照集合的順序來循環(huán)。而unordered則是在并發(fā)條件下,取消必須按集合順序循環(huán)。

//foreachOrdered 嚴格按照list的順序輸出(一般只有并行的時候用)
        peopleList.stream().parallel().forEach(System.out::println);
        System.out.println("----------------------------");
        // 取消嚴格按照順序循環(huán),有利于執(zhí)行效率的提高
        peopleList.stream().unordered().parallel().forEach(System.out::println);
  1. reduce
//reduce 1.第一個重載函數(shù)的入?yún)⑹且粋€BinaryOperator的函數(shù)式接口(該函數(shù)式接口接收兩個T類型的參數(shù)并返回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 把當前流設(shè)置為并行,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就是為了并行遍歷元素而設(shè)計的一個迭代器,jdk1.8中的集合框架中的數(shù)據(jù)結(jié)構(gòu)都默認實現(xiàn)了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("數(shù)值:"+num+"------"+threadName);
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                System.out.println("線程"+threadName+"運行結(jié)束-----");
            }).start();
        }
//得到的結(jié)果
線程Thread-2開始運行-----
線程Thread-0開始運行-----
數(shù)值:100------Thread-0
數(shù)值:88------Thread-2
線程Thread-3開始運行-----
線程Thread-1開始運行-----
線程Thread-1運行結(jié)束-----
線程Thread-3運行結(jié)束-----
線程Thread-0運行結(jié)束-----
數(shù)值:99------Thread-2
數(shù)值:100------Thread-2
數(shù)值:85------Thread-2
線程Thread-2運行結(jié)束-----
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容