java 流使用

前言

在本章中,你將會看到許多Stream API支持的許多操作.這些操作能讓你快速完成許多複雜的查詢.如篩選、切片、映射、查找、匹配和歸約。 接下來,我們會看到一些特殊的流:數值流,來自文件和數組等多種來源的流,最後是無限流.

1 篩選和切片

在本節中,我們來看看如何選擇流中的元素:用謂詞篩選,篩選出各不相同的元素,忽略流 中的頭幾個元素,或將流截短至指定長度。

1.1 用謂詞篩選

filter()方法:

該操作會接受一個謂詞作爲參數,並返回一個包含所有符合謂詞元素的流. 例如,你可以像下圖所示的這樣,篩選出所有素菜,創建一張素食菜單:

@Test
    public void test2() {

        List<Dish> vegetarianMenu =
                menu.stream()
                        .filter(Dish::isVegetarian)
                        .collect(toList());
        System.out.println(vegetarianMenu);
    }

在這裏插入圖片描述

1.2 篩選各異的元素

distinct()方法: 它會返回一個元素各異的流(根據流所生成元素的hashCode 和 equals 方法實現),

例如,以下代碼會篩選出列表中所有的偶數,並確保沒有重複。

@Test
public void test3() {
List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);

   numbers.stream()
            .filter(i -> i % 2 == 0)
            .distinct()
            .forEach(System.out::println);


}

在這裏插入圖片描述

1.3 截短流

limit()方法: 該方法會返回一個不超過給定長度的流。所需的長度作爲參數傳遞 給 limit 。如果流是有序的,則最多會返回前 n 個元素。

List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());

你可以看到,該方法只選出了符合謂詞的頭三個元素, 然後就立即返回了結果。

請注意 limit 也可以用在無序流上,比如源是一個 Set 。這種情況下, limit 的結果不會以 任何順序排列

在這裏插入圖片描述

1.4 跳過元素

流還支持 skip(n) 方法,返回一個扔掉了前 n 個元素的流。如果流中元素不足 n 個,則返回一 個空流。請注意, limit(n) 和 skip(n) 是互補的!

 @Test
    public void test4() {

        List<Dish> vegetarianMenu = menu.stream().filter(dish -> dish.getCalories() > 300)
                .skip(2)
                .collect(toList());

        System.out.println(vegetarianMenu);
    }

在這裏插入圖片描述

2 映射

一個非常常見的數據處理套路就是從某些對象中選擇信息.比如在sql中,你可以選擇一列. Stream API也通過 map()和flatMap()方法提供了類似的工具.

2.1 對流中每一個元素應用函數

map()

它會接受一個函數作爲參數,這個函數會被應用到每個元素上.並將其映射成一個新的元素.

例如,下面的代碼把方法引用 Dish::getName 傳給了 map 方法, 來提取流中菜餚的名稱:

@Test
    public void test5() {

        List<String> vegetarianMenu =
                menu.stream()
                        .map(Dish::getName)
                        .collect(toList());

        System.out.println(vegetarianMenu);
    }

因爲 getName 方法返回一個 String ,所以 map 方法輸出的流的類型就是 Stream 。

讓我們看一個稍微不同的例子來鞏固一下對 map 的理解。給定一個單詞列表,你想要返回另 一個列表,顯示每個單詞中有幾個字母。怎麼做呢?你需要對列表中的每個元素應用一個函數。 這聽起來正好該用 map 方法去做!

@Test
    public void test6() {

        List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
        List<Integer> integers = words.stream()
                .map(String::length)
                .collect(toList());

        System.out.println(integers);
    }

結果:
[6, 7, 2, 6]
現在讓我們回到提取菜名的例子。如果你要找出每道菜的名稱有多長,怎麼做?你可以像下 面這樣,再鏈接上一個 map :

@Test
    public void test7() {

       List<Integer> integers = menu.stream()
               .map(Dish::getName)
               .map(String::length)
               .collect(toList());
       System.out.println(integers);
   }

結果:

[4, 4, 7, 12, 4, 12, 5, 6, 6]

2.2 流的扁平化

你已經看到如何使用 map 方法返回列表中每個單詞的長度了。讓我們拓展一下:對於一張單 詞 表 , 如 何 返 回 一 張 列 表 , 列 出 裏 面 各 不 相 同 的 字 符 呢 ? 例 如 , 給 定 單 詞 列 表 [“Hello”,“World”] ,你想要返回列表 [“H”,“e”,“l”, “o”,“W”,“r”,“d”] 。

你可能首先想到這樣做

@Test
    public void test9() {
       List<String> words = Arrays.asList("Hello","World");
       List<String[]> list = words.stream()
               .map(word -> word.split(""))
               .distinct()
               .collect(toList());

       System.out.println(list);

   }

這個方法的問題在於,傳遞給 map 方法的Lambda爲每個單詞返回了一個 String[] ( String 列表)。因此, map 返回的流實際上是 Stream<String[]> 類型的。你真正想要的是用 Stream 來表示一個字符流.
在這裏插入圖片描述

結果;

[[Ljava.lang.String;@1def03a, [Ljava.lang.String;@122cdd0]

使用 flatMap()

 @Test
    public void test11() {
       List<String> words = Arrays.asList("Hello","World");
       List<String> list = words.stream()
               .map(word -> word.split(""))
               .flatMap(Arrays::stream)
               .distinct()
               .collect(toList());

       System.out.println(list);

   }

結果:

[H, e, l, o, W, r, d]

要想搞清楚 flatMap() 的效果,我們首先來看看 Arrays.stream() 方法

@Test
    public void test10() {
       List<String> words = Arrays.asList("Hello","World");
       List<Stream<String>> list = words.stream()
               .map(word -> word.split(""))
               .map(Arrays::stream)
               .distinct()
               .collect(toList());

       System.out.println(list);

   }

結果:

[java.util.stream.ReferencePipeline$Head@1591d15, java.util.stream.ReferencePipeline$Head@1ae6ba4]

可以看出,Arrays.stream() 的方法可以接受一個數組併產生一個流.

而 flatMap(Arrays::stream) 的效果就是 把兩個流合併起來,即扁平化爲一個流.

在這裏插入圖片描述

flatMap() 的參數是 Stream類型.

   <R> Stream<R> map(Function<? super T, ? extends R> mapper);

   <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

3 查找和匹配

另一個常見的數據處理套路是看看數據集中的某些元素是否匹配一個給定的屬性。Stream API通過 allMatch 、 anyMatch 、 noneMatch 、 findFirst 和 findAny 方法提供了這樣的工具

3.1 檢查謂詞是否至少匹配一個元素

anyMatch()

@Test
    public void test12() {
       boolean b = menu.stream().anyMatch(Dish::isVegetarian);
       if(b){
           System.out.println("有素菜!");

       }

   }

anyMatch 方法可以回答“流中是否有一個元素能匹配給定的謂詞”。

anyMatch 方法返回一個 boolean ,因此是一個終端操作.

3.2 檢查謂詞是否匹配所有元素

allMatch()

@Test
    public void test13() {
        boolean b = menu.stream()
                .allMatch(dish -> dish.getCalories() < 1000);
        if (b) {
            System.out.println("沒有高熱量食物,喫!");
        }

    }

noneMatch()

和 allMatch 相對的是 noneMatch 。它可以確保流中沒有任何元素與給定的謂詞匹配

@Test
    public void test14() {
        boolean b = menu.stream().noneMatch(dish -> {
            return dish.getCalories() > 1000;
        });
        if (b) {
            System.out.println("沒有高熱量食物,喫!");
        }

    }

有些操作不需要處理整個流就能得到結果。例如,假設你需要對一個用 and 連起來的大布 爾表達式求值。不管表達式有多長,你只需找到一個表達式爲 false ,就可以推斷整個表達式 將返回 false ,所以用不着計算整個表達式。這就是 短路。

對於流而言,某些操作(例如 allMatch 、 anyMatch 、 noneMatch 、 findFirst 和 findAny ) 不用處理整個流就能得到結果。只要找到一個元素,就可以有結果了。同樣, limit 也是一個 短路操作:它只需要創建一個給定大小的流,而用不着處理流中所有的元素。在碰到無限大小 的流的時候,這種操作就有用了:它們可以把無限流變成有限流.

3.3 查找元素

findAny()

 @Test
    public void test15() {
        Optional<Dish> any = menu.stream()
                .filter(dish -> dish.getCalories() == 450)
                .findAny();
        System.out.println(any);

       any.ifPresent(dish -> {
            System.out.println(dish.getCalories());
        });

    }

結果:

Optional[Dish{name='salmon', vegetarian=false, calories=450, type=FISH}]
450

可以看到 findAny() 返回一個 Optional 類型的值,

Optional 簡介 Optional 類( java.util.Optional )是一個容器類,代表一個值存在或不存在。在
上面的代碼中, findAny 可能什麼元素都沒找到。Java 8的庫設計人員引入了 Optional ,這
樣就不用返回衆所周知容易出問題的 null 了。

Optional 裏面幾種可以迫使你顯式地檢查值是否存在或處理值不存在的情形的方法也不錯。

  • isPresent() 將在 Optional 包含值的時候返回 true , 否則返回 false 。
  • ifPresent(Consumer block) 會在值存在的時候執行給定的代碼塊。我們在第3章 介紹了 Consumer 函數式接口;它讓你傳遞一- 個接收 T 類型參數,並返回 void 的Lambda 表達式。
  • T get() 會在值存在時返回值,否則拋出一個 NoSuchElement 異常。
  • T orElse(T other) 會在值存在時返回值,否則返回一個默認值。

3.4 查找第一個元素

FindFirst()

用來找到流中的第一個元素,它的工作方式類似於 findany

@Test
    public void test16() {
        List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> firstSquareDivisibleByThree =
                someNumbers.stream()
                        .map(x -> x * x)
                        .filter(x -> x % 3 == 0)
                        .findFirst(); // 9
        System.out.println(firstSquareDivisibleByThree);
        firstSquareDivisibleByThree.ifPresent(System.out::println);

    }

結果:

Optional[9]
9

何時使用 findFirst 和 findAny???

你可能會想,爲什麼會同時有 findFirst 和 findAny 呢?答案是並行。找到第一個元素 在並行上限制更多。如果你不關心返回的元素是哪個,請使用 findAny ,因爲它在使用並行流 時限制較少。

4 歸約

到目前爲止,你所見到的終端操作都返回一個boolean(allMatch之類),void(forEach),或Optional對象(findOny等).你也見過了可以用collect來將流中的元素合成一個List.

接下來,你將會看到如何把一個流中的元素組合起來,比如“計算菜單中的總卡路里”或“菜單中卡路里最高的菜是哪一個”.

此類查詢需要將流反覆組合起來,得到一個值比如Integer,這樣的查詢可被歸類爲 歸約 操作.

用函數式編程的術語來說,這稱爲摺疊(fold),因爲你可以講這個流看作一張長長的紙(你的流),反覆摺疊成一個小方塊,這就是摺疊操作的結果.

4.1 元素求和

reduce()

先看使用java8之前的 for-each 的做法

int sum = 0;
for (int x : numbers) {
    sum += x;
}

numbers中的每一個元素都使用加法運算反覆迭代來得到結果.通過反覆使用加法,你把一個數字列表歸約成了一個數字.

要是還能把所有的數字相乘,而不必去複製粘貼這段代碼,豈不是很好?這正是 reduce 操 作的用武之地,它對這種重複應用的模式做了抽象。你可以像下面這樣對流中所有的元素求和

@Test
    public void test17() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Integer reduce = numbers.stream().reduce(0, (a, b) -> a + b);

        System.out.println(reduce);//15


    }

reduce 接受兩個參數:

  • 一個初始值,這裏是0

  • 一個 BinaryOperator 來將兩個元素結合起來產生一個新值,這裏我們用的是 lambda (a, b) -> a + b
    上面的操作效果我們可以理解爲:把初始值和後面的函數每一次計算的結果相加求和.

    @Test
    public void test17() {
    List numbers = Arrays.asList(1, 2, 3, 4, 5);
    Integer reduce = numbers.stream()
    .reduce(1, (a,b)->a*b);
    System.out.println(reduce);//120

      }
    

這個操作效果我們可以理解爲:把初始值和後面的函數每一次計算的結果相乘.

通過這兩個例子我們可以看到:reduce兩個參數的運算方式取決於後面Lambda的運算方式
在這裏插入圖片描述

Lambda反覆結合每個元素,直到流被歸約成一個值.

在java8中Integer類多了幾個靜態方法,

/**
     * Adds two integers together as per the + operator.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the sum of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int sum(int a, int b) {
        return a + b;
    }

    /**
     * Returns the greater of two {@code int} values
     * as if by calling {@link Math#max(int, int) Math.max}.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the greater of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int max(int a, int b) {
        return Math.max(a, b);
    }

    /**
     * Returns the smaller of two {@code int} values
     * as if by calling {@link Math#min(int, int) Math.min}.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the smaller of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int min(int a, int b) {
        return Math.min(a, b);
    }

這不正好可以讓我們結合Lambda表達式嗎!(可能java8的設計者特意增加了這幾個操作適應Lambda編程需要),總之我們代碼看起來可以更簡潔

@Test
    public void test17() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Integer reduce = numbers.stream()
                .reduce(0, Integer::sum);
        System.out.println(reduce);

    }

無初始值

reduce還有一個重載的載體,它不接受初始值,但是會返回一個 Optional 對象.

@Test
    public void test18() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> optional = numbers.stream()
                .reduce(Integer::sum);
        System.out.println(optional);

    }

爲什麼它返回一個 Optional 呢?考慮到流中沒有值的情況,reduce操作便無法返回其和,因爲沒有初始值.這就是爲什麼結果被包裹在一個Optional對象裏,以表明結果可能不存在.

4.2 求最大值和最小值

@Test
    public void test19() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> max = numbers.stream()
                .reduce(Integer::max);
        Optional<Integer> min = numbers.stream().reduce(Integer::min);

        System.out.println("max="+max+",min="+min);

    }

給定兩個元素能夠返回最大值的Lambda,redce會考慮流中的新值和下一個元素,併產生一個新的最大值,直到整個流消耗完.

歸約方法的優勢與並行化

相比於java8之前的迭代求和,我們使用reduce的好處在於,這裏的迭代被內部迭代抽象掉了,這讓內部迭代可以選擇並行執行reduce操作. 而迭代求和的例子要更新共享變量sum,這不是那麼容易並行化的. 如果你加入了同步,可能會發現線程競爭抵消掉了並行本應該帶來的性能提升.

這種計算的並行化需要另一種方法:將輸入分塊,再分塊求和,最後再合併起來.

這正式reduce所提供的,使用流來對所有的元素並行求和時,你的代碼幾乎不用修改: stream() 換成了 parallelStream().

付諸實踐

我們先會議一下目前爲止講到的所有流操作

在這裏插入圖片描述

public class TradeTest {


    Trader raoul = new Trader("Raoul", "Cambridge");
    Trader mario = new Trader("Mario", "Milan");
    Trader alan = new Trader("Alan", "Cambridge");
    Trader brian = new Trader("Brian", "Cambridge");

    List<Transaction> transactions = Arrays.asList(
            new Transaction(brian, 2011, 300),
            new Transaction(raoul, 2012, 1000),
            new Transaction(raoul, 2011, 400),
            new Transaction(mario, 2012, 710),
            new Transaction(mario, 2012, 700),
            new Transaction(alan, 2012, 950)
    );


    //(1) 找出2011年發生的所有交易,並按交易額排序(從低到高)。
    @Test
    public  void test1(){

        List<Transaction> list = transactions.stream()
                .filter(transaction -> transaction.getYear() == 2011)
                .sorted(Comparator.comparing(Transaction::getValue))
                .collect(toList());

        System.out.println(list);

    }

    //(2) 交易員都在哪些不同的城市工作過?
    @Test
    public void test2(){

        List<String> list = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getCity)
                .distinct()
                .collect(toList());
        System.out.println(list);

        List<String> list1 = transactions.stream()
                .map(transaction -> transaction.getTrader().getCity())
                .distinct()
                .collect(toList());
        System.out.println(list1);


    }

    //(3) 查找所有來自於劍橋的交易員,並按姓名排序。
    @Test
    public void test3(){
        List<Trader> list = transactions.stream()
                .map(Transaction::getTrader)
                .filter(trader -> trader.getCity().equals("Cambridge"))
                .sorted(Comparator.comparing(Trader::getName))
                .collect(toList());
        System.out.println(list);

        List<Trader> list1 = transactions.stream()
                .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getTrader)
                .sorted(Comparator.comparing(Trader::getName))
                .collect(toList());

        System.out.println(list1);


    }

    //(4) 返回所有交易員的姓名字符串,按字母順序排序。
    @Test
    public void test4(){
        List<String> list = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getName)
                .sorted((s1, s2) -> s1.compareTo(s2))
                //.distinct()
                .collect(toList());
        System.out.println(list);


        List<String> list1 = transactions.stream()
                .map(transaction -> transaction.getTrader().getName())
                .sorted((s1, s2) -> s1.compareTo(s2))
                .collect(toList());
        System.out.println(list1);


    }

    //(5) 有沒有交易員是在米蘭工作的?
    @Test
    public void test5(){
        boolean b = transactions.stream()
                .map(Transaction::getTrader)
                .anyMatch(trader -> trader.getCity().equals("Milan"));
        System.out.println(" 有沒有交易員是在米蘭工作的?="+b);

        boolean b1 = transactions.stream()
                .anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"));
        System.out.println(" 有沒有交易員是在米蘭工作的?="+b1);


    }

    //(6) 打印生活在劍橋的交易員的所有交易額。
    @Test
    public void test6(){
        List<Integer> list = transactions.stream()
                .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .collect(toList());
        System.out.println(list);
    }

    // (7) 所有交易中,最高的交易額是多少?
    @Test
    public void test7(){
        Optional<Integer> reduce = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::max);
        System.out.println(reduce);

    }

    //(8) 找到交易額最小的交易。
    @Test
    public void test8(){
        Optional<Integer> reduce = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::min);
        System.out.println(reduce);
    }
     //(9) 計算交易總額
        @Test
        public void test9(){
            Integer reduce = transactions.stream()
                    .map(transaction -> transaction.getValue())
                    .reduce(0, Integer::sum);
            System.out.println(reduce);
        }


}

5 數值流

我們先看下這段代碼有什麼問題

 //(9) 計算交易總額
    @Test
    public void test9(){
        Integer reduce = transactions.stream()
                .map(transaction -> transaction.getValue())
                .reduce(0, Integer::sum);
        System.out.println(reduce);
    }

這段代碼的問題是,它有一個暗含的裝箱成本。每個 Integer 都必須拆箱成一個原始類型, 再進行求和

Java 8引入了三個原始類型特化流接口來解決這個問題:

5.1 原始類型流特化

IntStream,DoubleStream,LongStream 分別將流中的元素特化爲 int ,double, long ,從而避免了暗含的裝箱成本.

每個接口都帶來了進行常用數值歸約的新方法,比如對數值流求和的 sum ,找到最大元素的 max 。 此外還有在必要時再把它們轉換回對象流的方法。

映射到數值流

將流轉化爲特化版本的常用方法爲 mapToInt(),mapToDouble(),mapToLong().

這些方法和前面說的 map 方法的工作方式一樣,只是它們返回的是一個特化流,而不是 Stream

@Test
    public void test20() {
        int sum = menu.stream() //返回一個Stream<Dish>
                .mapToInt(Dish::getCalories) //返回一個IntStream
                .sum();

        System.out.println(sum);

    }

請注意,如果流是空的, sum 默認返回 0 。 IntStream 還支持其他的方便方法,如max 、 min 、 average 等。

轉換回對象流 boxed()

同樣,一旦有了數值流,你可能會想把它轉換回非特化流。

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();

默認值 OptionalInt

要找到 IntStream 中的最大元素,可以調用 max 方法,它會返回一個 OptionalInt :

 OptionalInt optionalInt = menu.stream()
                .mapToInt(Dish::getCalories)
                .max();

現在,如果沒有最大值的話,你就可以顯式處理 OptionalInt 去定義一個默認值了:

@Test
    public void test21() {
        OptionalInt optionalInt = menu.stream()
                .mapToInt(Dish::getCalories)
                .max();

        int max = optionalInt.orElse(0);


        System.out.println(max);

    }

數值範圍

和數字打交道時,有一個常用的東西就是數值範圍。比如,假設你想要生成1和100之間的所有數字.

java8引入了兩個可用於 IntStream 和LongStream的靜態方法,range 和 rangeclosed.

這兩個方法都是第一個參數接受起始值,第二個參數接受結束值,但range是不包含結束值的, rangeclosed包含結束值

 @Test
    public void test22() {

        IntStream intStream = menu.stream()
                .mapToInt(Dish::getCalories);

        IntStream intStream1 = IntStream.range(1, 100)
                .filter(n -> n % 2 == 0);
        System.out.println(intStream1.count());
    }

6 構建流

本節將介紹如何從值序列、數組、文件來創建流,甚至由生成函數來創建無限流!

6.1 由值創建流

Stream.of()
你可以通過靜態方法 Stream.of() 通過顯示值創建一個流,他可以接受任意數量的參數.

@Test
    public void test23() {

        Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
        stream.map(String::toUpperCase).forEach(System.out::println);
    }

6.2 由數組創建流

Arrays.stream()

@Test
    public void test25() {

        int[] numbers = {2, 3, 5, 7, 11, 13};
        IntStream intStream = Arrays.stream(numbers);
        int sum = intStream.sum();
        System.out.println(sum);
    }

Arrays.stream() 有很多重載方法.
在這裏插入圖片描述

6.3 由文件創建流

Stream API 提供了兩個靜態方法來從函數生成流: Stream.iterate和Stream.generate.

這兩個操作可以創建所謂的無限流:不像從固定集合創建的流那樣有固定大小的流。由 iterate 和 generate 產生的流會用給定的函數按需創建值,因此可以無窮無盡地計算下去!一般來說, 應該使用 limit(n) 來對這種流加以限制,以避免打印無窮多個值.

迭代 iterate

@Test
    public void test27() {
    Stream.iterate(0,n->n+2)
            .limit(10)
            .forEach(System.out::println);
    }

iterate 方法接受一個初始值,還有一個作用於每次產生新值上的Lambda( UnaryOperator 類型), 這裏,我們使用Lambda n -> n + 2 ,返回的是前一個元 素加上2。因此, iterate 方法生成了一個所有正偶數的流.

這種iterate基本上是順序的,因爲結果取決於前一次應用,請注意,此操作將生成一個無限流——這個流沒有結尾,因爲值是按需計算的,可以永遠計算下去。我們說這個流是無界的。

生成 generate

與 iterate 方法類似, generate 方法也可讓你按需生成一個無限流。但 generate 不是依次 對每個新生成的值應用函數的。它接受一個 Supplier 類型的Lambda提供新的值。我們先來 看一個簡單的用法:

Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);

小結:

以下是你應從本文中學到的關鍵概念

  • Streams API可以表達複雜的數據處理查詢。常用的流操作總結在表5-1中

  • 你可以使用 filter ,distinct ,limit ,skip對流做篩選和切片.

  • 你可以使用map 和 flstMap 提取或者轉換流中的元素

  • 你可以使用 findFirst和findAny 查找流中的元素

  • 你可以使用anyMatch ,allMatch ,noneMatch 讓流匹配給定的謂詞

  • 上面這些方法都利用了短路:找到結果就立即停止計算,沒有必要處理整個流.

  • 你可以使用 reduce 方法將流中的所有元素進行迭代合併成一個結果,例如求和或者查找最大元素

  • filter和map操作是無狀態的,他們並不存儲任何狀態. reduce 操作要存儲狀態才能計算出一個值, sorted和distinct也要存儲狀態,因爲他們把流中所有元素緩存起來才能返回一個新的流. 這種操作稱爲有狀態操作.

  • 流有三種基本的原始類型特化: IntStream 、 DoubleStream 和 LongStream 。它們的操作也有相應的特化

  • 流不僅可以從集合創建,也可從值、數組、文件以及 iterate 與 generate 等特定方法創建。

  • 無限流是沒有固定大小的流

引用 java8實戰, github

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