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 詳解