Java?Stream流語(yǔ)法示例詳解
如何使用Stream?
聚合操作是Java 8針對(duì)集合類(lèi),使編程更為便利的方式,可以與Lambda表達(dá)式一起使用,達(dá)到更加簡(jiǎn)潔的目的。
前面例子中,對(duì)聚合操作的使用可以歸結(jié)為3個(gè)部分:
1) 創(chuàng)建Stream:通過(guò)stream()方法,取得集合對(duì)象的數(shù)據(jù)集。
2) Intermediate:通過(guò)一系列中間(Intermediate)方法,對(duì)數(shù)據(jù)集進(jìn)行過(guò)濾、檢索等數(shù)據(jù)集的再次處理。如上例中,使用filter()方法來(lái) 對(duì)數(shù)據(jù)集進(jìn)行過(guò)濾。
3) Terminal通過(guò)最終(terminal)方法完成對(duì)數(shù)據(jù)集中元素的處理。如上例中,使用forEach()完成對(duì)過(guò)濾后元素的打印。
在一次聚合操作中,可以有多個(gè)Intermediate,但是有且只有一個(gè)Terminal。也就是說(shuō),在對(duì)一個(gè)Stream可以進(jìn)行多次轉(zhuǎn)換操作,并不是每次都對(duì)Stream的每個(gè)元素執(zhí)行轉(zhuǎn)換。并不像for循環(huán)中,循環(huán)N次,其時(shí)間復(fù)雜度就是N。轉(zhuǎn)換操作是lazy(惰性求值)的,只有在Terminal操作執(zhí)行時(shí),才會(huì)一次性執(zhí)行??梢赃@么認(rèn)為,Stream 里有個(gè)操作函數(shù)的集合,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個(gè)集合中,在 Terminal 操作的時(shí)候循環(huán) Stream 對(duì)應(yīng)的集合,然后對(duì)每個(gè)元素執(zhí)行所有的函數(shù)。
Stream的操作分類(lèi)
剛才提到的Stream的操作有Intermediate、Terminal和Short-circuiting:
Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered
Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
Short-circuiting: anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
1、創(chuàng)建流
如果是數(shù)組的話(huà),可以使用 Arrays.stream()
或者 Stream.of()
創(chuàng)建流;如果是集合的話(huà),可以直接使用 stream()
方法創(chuàng)建流,因?yàn)樵摲椒ㄒ呀?jīng)添加到 Collection 接口中。
public class CreateStreamDemo { public static void main(String[] args) { String[] arr = new String[]{"武漢加油", "中國(guó)加油", "世界加油"}; Stream<String> stream = Arrays.stream(arr); stream = Stream.of("武漢加油", "中國(guó)加油", "世界加油"); List<String> list = new ArrayList<>(); list.add("武漢加油"); list.add("中國(guó)加油"); list.add("世界加油"); stream = list.stream(); } }
查看 Stream 源碼的話(huà),你會(huì)發(fā)現(xiàn) of() 方法內(nèi)部其實(shí)調(diào)用了 Arrays.stream() 方法。
public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
另外,集合還可以調(diào)用 parallelStream() 方法創(chuàng)建并發(fā)流,默認(rèn)使用的是 ForkJoinPool.commonPool()線(xiàn)程池。
List<Long> aList = new ArrayList<>(); Stream<Long> parallelStream = aList.parallelStream();
2、操作流
Stream 類(lèi)提供了很多有用的操作流的方法,我來(lái)挑一些常用的給你介紹一下。
1)過(guò)濾
通過(guò) filter() 方法可以從流中篩選出我們想要的元素。
public class FilterStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); Stream<String> stream = list.stream().filter(element -> element.contains("王")); stream.forEach(System.out::println); } }
filter() 方法接收的是一個(gè) Predicate(Java 8 新增的一個(gè)函數(shù)式接口,接受一個(gè)輸入?yún)?shù)返回一個(gè)布爾值結(jié)果)類(lèi)型的參數(shù),因此,我們可以直接將一個(gè) Lambda 表達(dá)式傳遞給該方法,比如說(shuō) element -> element.contains("王") 就是篩選出帶有“王”的字符串。
forEach() 方法接收的是一個(gè) Consumer(Java 8 新增的一個(gè)函數(shù)式接口,接受一個(gè)輸入?yún)?shù)并且無(wú)返回的操作)類(lèi)型的參數(shù),類(lèi)名 :: 方法名是 Java 8 引入的新語(yǔ)法,System.out 返回 PrintStream 類(lèi),println 方法你應(yīng)該知道是打印的。
stream.forEach(System.out::println); 相當(dāng)于在 for 循環(huán)中打印,類(lèi)似于下面的代碼:
for (String s : strs) { System.out.println(s); }
很明顯,一行代碼看起來(lái)更簡(jiǎn)潔一些。來(lái)看一下程序的輸出結(jié)果:
王力宏
2)映射
如果想通過(guò)某種操作把一個(gè)流中的元素轉(zhuǎn)化成新的流中的元素,可以使用 map() 方法。
public class MapStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); Stream<Integer> stream = list.stream().map(String::length); stream.forEach(System.out::println); } }
map() 方法接收的是一個(gè) Function(Java 8 新增的一個(gè)函數(shù)式接口,接受一個(gè)輸入?yún)?shù) T,返回一個(gè)結(jié)果 R)類(lèi)型的參數(shù),此時(shí)參數(shù) 為 String 類(lèi)的 length 方法,也就是把 Stream<String> 的流轉(zhuǎn)成一個(gè) Stream<Integer> 的流。
程序輸出的結(jié)果如下所示:
3
3
2
3
3)匹配
Stream 類(lèi)提供了三個(gè)方法可供進(jìn)行元素匹配,它們分別是:
anyMatch(),只要有一個(gè)元素匹配傳入的條件,就返回 true。
allMatch(),只有有一個(gè)元素不匹配傳入的條件,就返回 false;如果全部匹配,則返回 true。
noneMatch(),只要有一個(gè)元素匹配傳入的條件,就返回 false;如果全部匹配,則返回 true。
public class MatchStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("王")); boolean allMatchFlag = list.stream().allMatch(element -> element.length() > 1); boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉")); System.out.println(anyMatchFlag); System.out.println(allMatchFlag); System.out.println(noneMatchFlag); } }
因?yàn)?ldquo;王力宏”以“王”字開(kāi)頭,所以 anyMatchFlag 應(yīng)該為 true;因?yàn)?ldquo;周杰倫”、“王力宏”、“陶喆”、“林俊杰”的字符串長(zhǎng)度都大于 1,所以 allMatchFlag 為 true;因?yàn)?4 個(gè)字符串結(jié)尾都不是“沉”,所以 noneMatchFlag 為 true。
程序輸出的結(jié)果如下所示:
true
true
true
4)組合
reduce() 方法的主要作用是把 Stream 中的元素組合起來(lái),它有兩種用法:
Optional<T> reduce(BinaryOperator<T> accumulator)
沒(méi)有起始值,只有一個(gè)參數(shù),就是運(yùn)算規(guī)則,此時(shí)返回 Optional。
T reduce(T identity, BinaryOperator<T> accumulator)
有起始值,有運(yùn)算規(guī)則,兩個(gè)參數(shù),此時(shí)返回的類(lèi)型和起始值類(lèi)型一致。
來(lái)看下面這個(gè)例子。
public class ReduceStreamDemo { public static void main(String[] args) { Integer[] ints = {0, 1, 2, 3}; List<Integer> list = Arrays.asList(ints); Optional<Integer> optional = list.stream().reduce((a, b) -> a + b); Optional<Integer> optional1 = list.stream().reduce(Integer::sum); System.out.println(optional.orElse(0)); System.out.println(optional1.orElse(0)); int reduce = list.stream().reduce(6, (a, b) -> a + b); System.out.println(reduce); int reduce1 = list.stream().reduce(6, Integer::sum); System.out.println(reduce1); } }
運(yùn)算規(guī)則可以是 Lambda 表達(dá)式(比如 (a, b) -> a + b),也可以是類(lèi)名::方法名(比如 Integer::sum)。
程序運(yùn)行的結(jié)果如下所示:
6
6
12
12
0、1、2、3 在沒(méi)有起始值相加的時(shí)候結(jié)果為 6;有起始值 6 的時(shí)候結(jié)果為 12。
3、轉(zhuǎn)換流
既然可以把集合或者數(shù)組轉(zhuǎn)成流,那么也應(yīng)該有對(duì)應(yīng)的方法,將流轉(zhuǎn)換回去——collect() 方法就滿(mǎn)足了這種需求。
public class CollectStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰倫"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); String[] strArray = list.stream().toArray(String[]::new); System.out.println(Arrays.toString(strArray)); List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList()); List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new)); System.out.println(list1); System.out.println(list2); String str = list.stream().collect(Collectors.joining(", ")).toString(); System.out.println(str); } }
toArray() 方法可以將流轉(zhuǎn)換成數(shù)組,你可能比較好奇的是 String[]::new,它是什么東東呢?來(lái)看一下 toArray() 方法的源碼。
<A> A[] toArray(IntFunction<A[]> generator);
也就是說(shuō) String[]::new 是一個(gè) IntFunction,一個(gè)可以產(chǎn)生所需的新數(shù)組的函數(shù),可以通過(guò)反編譯字節(jié)碼看看它到底是什么:
String[] strArray = (String[])list.stream().toArray((x$0) -> { return new String[x$0]; }); System.out.println(Arrays.toString(strArray));
也就是相當(dāng)于返回了一個(gè)指定長(zhǎng)度的字符串?dāng)?shù)組。
當(dāng)我們需要把一個(gè)集合按照某種規(guī)則轉(zhuǎn)成另外一個(gè)集合的時(shí)候,就可以配套使用 map() 方法和 collect() 方法。
List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
通過(guò) stream() 方法創(chuàng)建集合的流后,再通過(guò) map(String:length) 將其映射為字符串長(zhǎng)度的一個(gè)新流,最后通過(guò) collect() 方法將其轉(zhuǎn)換成新的集合。
Collectors 是一個(gè)收集器的工具類(lèi),內(nèi)置了一系列收集器實(shí)現(xiàn),比如說(shuō) toList() 方法將元素收集到一個(gè)新的 java.util.List 中;比如說(shuō) toCollection() 方法將元素收集到一個(gè)新的 java.util.ArrayList 中;比如說(shuō) joining() 方法將元素收集到一個(gè)可以用分隔符指定的字符串中。
來(lái)看一下程序的輸出結(jié)果:
[周杰倫, 王力宏, 陶喆, 林俊杰]
[3, 3, 2, 3]
[周杰倫, 王力宏, 陶喆, 林俊杰]
周杰倫, 王力宏, 陶喆, 林俊杰
以上就是Java Stream流語(yǔ)法示例詳解的詳細(xì)內(nèi)容,更多關(guān)于JavaStream流的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
lambda表達(dá)式解決java后臺(tái)分組排序過(guò)程解析
這篇文章主要介紹了lambda表達(dá)式解決java后臺(tái)分組排序過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10十大常見(jiàn)Java String問(wèn)題_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文介紹Java中關(guān)于String最常見(jiàn)的10個(gè)問(wèn)題,需要的朋友參考下吧2017-04-04兩分鐘解決IntelliJ IDEA中文亂碼問(wèn)題(推薦)
這篇文章主要介紹了兩分鐘解決IntelliJ IDEA中文亂碼問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02SpringMVC深入講解文件的上傳下載實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了springMVC實(shí)現(xiàn)文件上傳和下載的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06JSON.toJSONString()空字段不忽略修改的問(wèn)題
這篇文章主要介紹了JSON.toJSONString()空字段不忽略修改的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02