Stream 流

1、概念

Stream是Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查找、過濾和映射數據等操作。使用Stream API對集合數據進行操作,就類似於使用SQL 執行的數據庫查詢。也可以使用Stream API來並行執行操作。簡而言之,Stream API提供了一-種高效且易於使用的處理數據的方式。

①Stream自己不會存儲元素
②Stream不會改變源對象。相反,他們會返回一個持有結果的新Stream
③Stream操作是延遲執行的。這意味着他們會等到需要結果的時候才執行

在這裏插入圖片描述

2、創建流

通過Collection系列集合提供的stream()或parallelStream( )

    @Test
    public void test1(){
        List<String> list=new ArrayList<>();
        Stream<String> stringStream=list.stream();
    }

通過Arrays中的靜態方法stream( )獲取數組流

    @Test
    public void test2(){
        Cat[] cats=new Cat[]{};
        Stream<Cat> catStream= Arrays.stream(cats);
    }

通過Stream類中的靜態方法of( )

    @Test
    public void test3(){
        Stream<String> stringStream=Stream.of("a","b","c");
    }

創建無限流

    @Test
    public void test4(){
        Stream<Integer> integerStream=Stream.iterate(0,x->x+2);
        integerStream.limit(10).forEach(System.out::println);
    }

3、中間操作

多箇中間操作可以連接起來形成-一個流水線, 除非在流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次性全部處理,稱爲“惰性求值”。

篩選和切片:

filter-接收Lambda,從流中排除某些元素;
limit-截斷流,使其元素不超過給定數量;
skip(n) -跳過元素,返回一個去除了前n個元素的流。若流中元素不足n個,則返回一個空流。與limit(n)互補;
distinct-篩選排重,通過流所生成元素的hashCode()和equals()去除重複元素。

舉個栗子

public class Cat {

    int age;
    String name;

    public Cat(){
    }

    public Cat(int age){
        this.age=age;
    }

    public Cat(int age,String name){
        this.age=age;
        this.name=name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

    @Test
    public void testStream(){
        List<Cat> list=Arrays.asList(
                new Cat(2,"Kitty"),
                new Cat(3,"Garfield"),
                new Cat(5,"Tom")
        );

      list.stream() // 創建流
                .filter(x->x.getAge()>2) // 中間操作
                .forEach(System.out::println); // 終止操作

    }

結果

Cat{age=3, name='Garfield'}
Cat{age=5, name='Tom'}

映射:

map - 接收Lambda ,將元素轉換成其他形式或提取信息。接收一個函數作爲參數,該函數會被應用到每個元素上,並將其映射成一個新的元素;
flatMap - 接收一個函數作爲參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流。

舉個栗子

    @Test
    public void testStreamMap(){
        Stream<String> stringStream=Stream.of("aaa","bbb","ccc");
        stringStream.map(x->x.toUpperCase())
                .forEach(System.out::println);
    }

輸出結果爲

AAA
BBB
CCC

再舉個栗子

    @Test
    public void testStreamMap2(){
        List<Cat> list=Arrays.asList(
                new Cat(2,"Kitty"),
                new Cat(3,"Garfield"),
                new Cat(5,"Tom")
        );

       list.stream().map(Cat::getName).forEach(System.out::println);
    }

輸出結果爲

Kitty
Garfield
Tom

排序:

sorted()-自然排序(Comparable);
sorted(Comparator com)- 定製排序(Comparator)。

查找與匹配:

allMatch-檢查是否匹配所有元素;
anyMatch-檢查是否至少匹配一個元素;
noneMatch-檢查是否沒有匹配所有元素;
findFirst-返回第一個元素;
findAny-返回當前流中的任意元素;
count-返回流中元素的總個數;
max-返回流中最大值;
min-返回流中最小值。

4、並行流和串行流

並行流就是把一個內容分成多個數據塊,並用不同的線程分別處理每個數據塊的流。
java8對並行流進行了一些優化,方便用戶很容易的對數據進行並行操作,Stream API 可以聲明性地通過 parallel() 與 sequential() 在並行流與順序流之間進行切換。

從JDK1.7開始,Java提供了ForkJoin框架用於並行地執行任務,它的思想就是將一大大的任務拆分成若干個小任務,最終彙總每個小任務的結果得到這個大任務的結果。
在這裏插入圖片描述
舉個栗子:假設現在我們需要對0到50000000000進行求和

栗子1:使用for循環進行單線程操作,CPU利用率低,耗時42911ms

    @Test
    public void test1() {
        Instant start = Instant.now();
        long sum = 0L;
        for (long i = 0; i <= 50000000000L; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗費時間爲: " + Duration.between(start, end).toMillis());
    }

在這裏插入圖片描述
栗子2:使用 ForkJoin 進行任務拆分,CPU利用率100%,耗時25225ms

public class ForkJoinCalculate extends RecursiveTask<Long> {
    private long start;
    private long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    /**
     * 運算或者 任務拆分
     * @return
     */
    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THRESHOLD) {
            //當前任務數據區間少於10000
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            //當前任務數據區間大於10000   進行任務拆分
            long middle = (start + end) / 2;
            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            left.fork();
            right.fork();
            return left.join() + right.join();
        }
    }
}

    @Test 
    public void test2() {
        Instant start = Instant.now();
        ForkJoinPool fjp = new ForkJoinPool( );
        ForkJoinTask<Long> task = new ForkJoinCalculate( 0 , 50000000000L ) ;
        Long sum = fjp.invoke(task);
        System.out.println(sum);
        Instant end = Instant.now();

        System.out.println( "耗費時間爲。"+Duration.between(start, end). toMillis());
    }

在這裏插入圖片描述
栗子3:使用Stream的並行流,使用起來更加簡單,也可以充分的利用CPU

    @Test
    public void test3(){
        Instant start = Instant . now();
        long res = LongStream.rangeClosed(0, 1000000L).
        parallel().reduce(0,Long::sum) ;
        System.out.println(res);
        Instant end = Instant.now();
        System.out.println( "耗費時間爲: "+Duration. between(start, end). toMillis());
    }

在這裏插入圖片描述
其實並行流的的底層使用的也是 ForkJoin 框架
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章