Optional 和 Stream

Optional

Optional 不是函數(shù)式接口,而是用于防止 NullPointerException 的一個(gè)工具類。

Optional 是一個(gè)簡(jiǎn)單的容器,其值可能是 null 或者不是 null。在 Java8 之前,一般某個(gè)函數(shù)應(yīng)該返回非空對(duì)象,但是有時(shí)卻什么也沒有返回,而在 Java8 中,你應(yīng)該返回 Optional 而不是 null。

// of():為非null的值創(chuàng)建一個(gè) Optional
Optional<String> optional = Optional.of("bam");

// isPresent():如果值存在返回true,否則返回false
optional.isPresent();  // true

// get():如果Optional有值則將其返回,否則拋出NoSuchElementException
optional.get();  // "bam"

// orElse():如果有值則將其返回,否則返回指定的其它值
optional.orElse("fallback");  // "bam"

// ifPresent():如果 Optional 實(shí)例有值,則為其調(diào)用 consumer,否則不做處理
optional.ifPresent((s) -> System.out.println(s.charAt(0)));  // "b"

推薦閱讀:Java8 如何正確使用Optional

Stream

java.util.Stream 表示能應(yīng)用在一組元素執(zhí)行的操作序列。Stream 操作分為中間操作和最終操作兩種,最終操作是返回特定類型的計(jì)算結(jié)果,而中間操作返回 Stream 本身,這樣你就可以將多個(gè)操作依次串起來。Stream 的創(chuàng)建需要指定一個(gè)數(shù)據(jù)源,比如 java.util.Collection 的子類,如 List 或者 Set,但不支持 Map 。Stream 的操作可以串行執(zhí)行或者并行執(zhí)行。

來看看 Stream 是怎么用,首先創(chuàng)建實(shí)例代碼用到的數(shù)據(jù) List:

List<String> stringList = new ArrayList<>();
stringList.add("ddd2");
stringList.add("aaa2");
stringList.add("bbb1");
stringList.add("aaa1");
stringList.add("bbb3");
stringList.add("ccc");
stringList.add("bbb2");
stringList.add("ddd1");

Java8 擴(kuò)展了集合類,可以通過 Collection.stream() 或者 Collection.parallelStream() 來創(chuàng)建一個(gè) Stream。下面幾節(jié)將詳細(xì)解釋常用的 Stream 操作:

Filter(過濾)

通過 predicate 接口來過濾并只保留符合條件的元素,該操作屬于中間操作,所以可以在過濾后的結(jié)果來應(yīng)用其他 Stream 操作(比如 forEach )。forEach 需要一個(gè)函數(shù)來對(duì)過濾后的元素依次執(zhí)行。forEach 是一個(gè)最終操作,所以不能在 forEach 之后來執(zhí)行其他 Stream 操作。

    // 測(cè)試 Filter(過濾)
    stringList.stream().filter((s) -> s.startsWith("a")).forEach(System.out::println);  // aaa2 aaa1

forEach 是為 Lambda 而設(shè)計(jì)的,保持了最緊湊的風(fēng)格,而且 Lambda 表達(dá)式本身是可以重用的,非常方便。

Sorted(排序)

排序是一個(gè) 中間操作,返回的是排序好后的 Stream。如果你不指定一個(gè)自定義的 Comparator 則會(huì)使用默認(rèn)排序。

    // 測(cè)試 Sort (排序)
    stringList.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println);// aaa1 aaa2

需要注意的是,排序只創(chuàng)建了一個(gè)排列好后的 Stream,而不會(huì)影響原有的數(shù)據(jù)源,排序之后原數(shù)據(jù) stringCollection 是不會(huì)被修改的:

    System.out.println(stringList);  // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Map(映射)

中間操作 map 會(huì)將元素根據(jù)指定的 Function 接口來依次將元素轉(zhuǎn)成另外的對(duì)象。

下面的示例展示了將字符串轉(zhuǎn)換為大寫字符串。你也可以通過 map 來將對(duì)象轉(zhuǎn)換成其他類型,map 返回的 Stream 類型是根據(jù) map 傳遞進(jìn)去的函數(shù)返回值決定的。

    // 測(cè)試 Map 操作
    stringList.stream().map(String::toUpperCase).sorted((a, b) -> b.compareTo(a)).forEach(System.out::println);  // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Match(匹配)

Stream 提供了多種匹配操作,允許檢測(cè)指定的 Predicate 是否匹配整個(gè) Stream。所有的匹配操作都是 最終操作 ,并返回一個(gè) boolean 類型的值。

    // 測(cè)試 Match (匹配)操作
    boolean anyStartsWithA = stringList.stream().anyMatch((s) -> s.startsWith("a"));
    System.out.println(anyStartsWithA);      // true

    boolean allStartsWithA = stringList.stream().allMatch((s) -> s.startsWith("a"));
    System.out.println(allStartsWithA);      // false

    boolean noneStartsWithZ = stringList.stream().noneMatch((s) -> s.startsWith("z"));
    System.out.println(noneStartsWithZ);     // true

Count(計(jì)數(shù))

計(jì)數(shù)是一個(gè) 最終操作,返回 Stream 中元素的個(gè)數(shù),返回值類型是 long。

    // 測(cè)試 Count (計(jì)數(shù))操作
    long startsWithB = stringList.stream().filter((s) -> s.startsWith("b")).count();
    System.out.println(startsWithB);    // 3

Reduce(規(guī)約)

這是一個(gè) 最終操作 ,允許通過指定的函數(shù)來講 stream 中的多個(gè)元素規(guī)約為一個(gè)元素,規(guī)約后的結(jié)果是通過 Optional 接口表示的:

    // 測(cè)試 Reduce (規(guī)約)操作
    Optional<String> reduced = stringList.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2);
    reduced.ifPresent(System.out::println);  // aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2

注: 這個(gè)方法的主要作用是把 Stream 元素組合起來。它提供一個(gè)起始值(種子),然后依照運(yùn)算規(guī)則(BinaryOperator),和前面 Stream 的第一個(gè)、第二個(gè)、第 n 個(gè)元素組合。從這個(gè)意義上說,字符串拼接、數(shù)值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相當(dāng)于 Integer sum = integers.reduce(0, (a, b) -> a+b); 也有沒有起始值的情況,這時(shí)會(huì)把 Stream 的前面兩個(gè)元素組合起來,返回的是 Optional。

// 字符串連接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); 

// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); 

// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);

// 求和,sumValue = 10, 無起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();

// 過濾,字符串連接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);

上面第一個(gè)示例的 reduce(),第一個(gè)參數(shù)(空白字符)即為起始值,第二個(gè)參數(shù)(String::concat)為 BinaryOperator。這類有起始值的 reduce() 都返回具體的對(duì)象。而對(duì)于第四個(gè)示例沒有起始值的 reduce(),由于可能沒有足夠的元素,返回的是 Optional。

推薦閱讀: Java8 中的 Streams API 詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自: Java 8 中的 Streams API 詳解 為什么需要 Stream Stream 作為 Java ...
    普度眾生的面癱青年閱讀 2,975評(píng)論 0 11
  • stream 什么是流 Stream 不是集合元素,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù),它是有關(guān)算法和計(jì)算的,它更像一個(gè)高...
    的盧嘯閱讀 746評(píng)論 0 0
  • 1、Stream簡(jiǎn)介 Stream作為Java 8的一大亮點(diǎn),是對(duì)集合(Collection)、數(shù)組對(duì)象功能的增強(qiáng)...
    Albert_Yu閱讀 9,183評(píng)論 1 21
  • 什么是Java8 Stream,為什么需要Stream? Stream是Java8一大亮點(diǎn),它與 java.io ...
    消失er閱讀 1,605評(píng)論 3 22
  • 了解Stream ? Java8中有兩個(gè)最為重要的改變,一個(gè)是Lambda表達(dá)式,另一個(gè)就是Stream AP...
    龍歷旗閱讀 3,429評(píng)論 3 4

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