什么是stream
Stream作為java8 的一大亮點(diǎn),與InputStream和OutPutStream是完全不同的概念。也不同于stax對(duì)xml解析的Stream,也不是Amazon kinesis對(duì)大數(shù)據(jù)實(shí)時(shí)處理的Stream。java8 中的Stream是對(duì)集合(Collection)對(duì)象功能的增強(qiáng)。專注于對(duì)集合對(duì)象進(jìn)行非常便利、高效的聚合操作。同時(shí)它提供串行和并行兩種模式,并發(fā)模式能夠充分利用多核處理器的優(yōu)勢(shì)。使用frok\join 并行方式拆分和加速任務(wù)。
什么是聚合操作
java代碼經(jīng)常不得不依賴于關(guān)系型數(shù)據(jù)庫(kù)的聚合操作來(lái)完成例如:
- 客戶每月平均消費(fèi)金額
- 最昂貴的在售商品
- 本周完成的有效訂單(排除無(wú)效的)
- 取十個(gè)數(shù)據(jù)樣本作為首頁(yè)推薦
這類操作。
在java7 中如果要發(fā)現(xiàn)type為grocery的所有交易,然后返回以交易值降序排序好的交易ID集合,需要這樣寫(xiě):
List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions) {
if(t.getType() == Transaction.GROCERY) {
groceryTransactions.add(t);
}
}
Collections.sort(groceryTransactions, new Comparator() {
public int compare(Transaction t1, Transaction t2) {
return t2.getValue().compareTo(t1.getValue());
}
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
transactionsIds.add(t.getId());
}
而在java8中使用Stream,代碼更加簡(jiǎn)潔易讀,而且使用并發(fā)模式,程序執(zhí)行速度更快。
List<Integer> transactionsIds = transactions.parallelStream().
filter(t - > t.getType() == Tranusaction.GROCERY).
sorted(comparing(Transaction :: getBalue).reversed()).
map(Transaction::getId).
collect(toList());
Stream 什么是流
Stream不是集合元素,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù),他是有關(guān)算法和計(jì)算的,更像一個(gè)高級(jí)版本的Iterator.原始版本的Iterator,用戶只能顯式地一個(gè)一個(gè)遍歷元素并對(duì)其執(zhí)行某些操作;高級(jí)版本的Stream,用戶只要給出需要對(duì)其包含的元素執(zhí)行什么操作。比如“過(guò)濾掉長(zhǎng)度大于10的字符串”,“獲取每個(gè)字符串的首字母”等,Stream會(huì)隱式的在內(nèi)部進(jìn)行遍歷,做出相應(yīng)的數(shù)據(jù)轉(zhuǎn)換。
Stream就如果一個(gè)迭代器(Iterator),單向,不可往復(fù),只能遍歷一次,遍歷一次后即用盡了,就好比流水從面前流過(guò),一區(qū)不復(fù)返
而和迭代器又不同的是,Stream可以并行化操作,Stream的并行操作依賴于jdk1.7引入的Fork/Join框架來(lái)拆分任務(wù)和加速處理過(guò)程。
流的構(gòu)成
當(dāng)我們使用一個(gè)流的時(shí)候,通常包括三個(gè)基本步驟:
- 獲取一個(gè)數(shù)據(jù)源(Source)
- 數(shù)據(jù)轉(zhuǎn)換
- 執(zhí)行操作獲取想要的結(jié)果
每次轉(zhuǎn)換原有Stream對(duì)象不改變,返回一個(gè)新的Stream對(duì)象,這就允許對(duì)其操作可以像鏈條一樣排列,變成一個(gè)管道。如下圖所示。

有多種方式生成Stream Source:
從Collection和數(shù)組
- Collection.stream()
- Collection.parallelStream()
- Arrays.stream(T array) or Stream.of()
從BufferedReader
- java.io.BufferedReader.lines()
靜態(tài)工廠
- java.util.stream.IntStream.range()
- java.nio.file.Files.walk()
自己構(gòu)建
- java.util.Spliterator
其他
- Random.ints()
- BitSet.stream()
- Pattern.splitAsStream(java.lang.CharSequence)
- JarFile.stream()
流的操作類型分為兩種
- Intermediate
- Terminal
Intermediate:一個(gè)流后面可以跟隨0個(gè)或多個(gè)intermediate操作。其主要目的是打開(kāi)流,做出某種程度的數(shù)據(jù)映射/過(guò)濾,然后返回一個(gè)新的流,交給下一個(gè)操作使用。這類操作都是惰性化的(lazy),也就是說(shuō),僅僅調(diào)用這類方法,并沒(méi)有真正開(kāi)始流的遍歷。
Terminal:一個(gè)流只能有一個(gè)terminal操作,當(dāng)這個(gè)操作執(zhí)行后,流就被使用“光”了。無(wú)法再被操作。所以這必定是流的最后一個(gè)操作。Terminal操作的執(zhí)行,才會(huì)真正開(kāi)始流的遍歷,并且會(huì)生成一個(gè)結(jié)果,或者一個(gè)side effect。
還有另一種操作被稱為short-circuiting 用以指:
- 對(duì)于一個(gè) intermediate 操作,如果它接受的是一個(gè)無(wú)限大(infinite/unbounded)的 Stream,但返回一個(gè)有限的新 Stream。
- 對(duì)于一個(gè) terminal 操作,如果它接受的是一個(gè)無(wú)限大的 Stream,但能在有限的時(shí)間計(jì)算出結(jié)果。
當(dāng)操作一個(gè)無(wú)限大的 Stream,而又希望在有限時(shí)間內(nèi)完成操作,則在管道內(nèi)擁有一個(gè) short-circuiting 操作是必要非充分條件。
我們可以這樣簡(jiǎn)單的理解,Stream里有個(gè)操作函數(shù)的集合,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個(gè)集合中,在Terminal操作的時(shí)候循環(huán)Stream對(duì)應(yīng)的函數(shù)集合,然后對(duì)每個(gè)元素執(zhí)行所有的函數(shù)。
一個(gè)流的操作示例
int sum = widgets.stream().
filter(w -> w.getColor() == RED).
mapToInt(w -> w.getWeight()).
sum();
stream() 獲取當(dāng)前widgets的source,filter和mapToInt為intermediate操作,進(jìn)行數(shù)據(jù)篩選和轉(zhuǎn)換,最后一個(gè)sun()為terminal操作,對(duì)符合條件的全部widget做重量(weight)的求和。
流的使用詳解
簡(jiǎn)單說(shuō),對(duì)Stream的使用就是實(shí)現(xiàn)了一個(gè)filter-map-reduce過(guò)程,產(chǎn)生一個(gè)最終的結(jié)果,或者導(dǎo)致以個(gè)副作用(side effect)【此處暫未理解】
流的構(gòu)造與轉(zhuǎn)換
構(gòu)造流的幾種常見(jiàn)方法
/*Stream 初始化的幾種方式*/
Stream stream = Stream.of("a","b","c");
/*Arrays*/
String[] strArray = new String[]{"a","b","c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
/*Collection*/
List<String> list = Arrays.asList(strArray);
stream = list.stream();
對(duì)于基本數(shù)值型,目前有三種對(duì)應(yīng)的包裝類型Stream:
- IntStream (Stream<Integer>)
- LongStream (Stream<Long>)
- DoubleStream (Stream<Double>)
但是裝箱和拆箱會(huì)很耗時(shí),所以特別為這三種基本數(shù)值型提供了對(duì)應(yīng)的Stream。
數(shù)值流的構(gòu)造
/*Array*/
String[] strArray1 = (String[]) stream.toArray(String[]::new);
/*Collection*/
List<String> list1 = (List<String>) stream.collect(Collectors.toList());
List<String> list2 = (List<String>) stream.collect(Collectors.toCollection(LinkedList::new));
Set<String> set = (Set<String>) stream.collect(Collectors.toSet());
Stack<String> stack = (Stack<String>) stream.collect(Collectors.toCollection(Stack::new));
/*String*/
String str = stream.collect(Collectors.joining()).toString();
/*一個(gè) Stream 只可以使用一次,上面的代碼為了簡(jiǎn)潔而重復(fù)使用了數(shù)次。*/
流的操作
接下來(lái),當(dāng)把一個(gè)數(shù)據(jù)結(jié)構(gòu)包裝成Stream后,就要開(kāi)始對(duì)立面的元素進(jìn)行各類的操作了。常見(jiàn)的操作可以歸類如下。
- Intermediate:
map(mapToInt,flatMap等)、filter、distinct,sorted、peek、limit、skip、parallel、sequential、unordered- Terminal
forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator- Short-circuiting:
anyMatch、allMatch、noneMatch、findFirst、findAny、limit
下面看一下Stream的比較典型的用法。
map/flatMap的作用就是把input Stream的每一個(gè)元素,映射成output Stream的另外一個(gè)元素。
轉(zhuǎn)換大寫(xiě)
List<String> output = wordList.stream().
map(String::toUpperCase).
collect(Collectors.toList());
平方數(shù)
List<Integer> nums = Arrays.asList(1,2,3,4);
List<Integer> result = nums.stream().
map(n -> n * n).
collect(Collectors.toList());
從上面例子可以看出,map生成的是個(gè)1:1的映射,每個(gè)輸入元素,都按照規(guī)則轉(zhuǎn)換成為另外一個(gè)元素。還有一些場(chǎng)景,是一對(duì)多的映射關(guān)系的。這時(shí)需要flatMap
一對(duì)多映射
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(2,3),
Arrays.asList(4,5,6));
Stream<Integer> outputStream = inputStream.
flatMap(Collection::stream);
flatMap把inputStream中的層級(jí)結(jié)構(gòu)扁平化,就是將最底層元素抽出來(lái)放到一起,最終output的心Stream里面已經(jīng)沒(méi)有l(wèi)ist了 都是直接的數(shù)字。
filter對(duì)原始Stream進(jìn)行某項(xiàng)測(cè)試,通過(guò)測(cè)試的元素被留下來(lái)生成一個(gè)新的Stream。
留下偶數(shù)
Integer[] sixNums = {1,2,3,4,5,6};
Integer[] evens = Stream.of(sixNums).
filter(n -> n%2 == 0).
toArray(Integer[]::new);
把單詞挑出來(lái)
BufferedReader reader = new BufferedReader(new ASCIIReader(null,null,null));
List<String> output1 = reader.lines().
flatMap(line -> Stream.of(line.split(","))).
filter(word -> word.length() > 0).
collect(Collectors.toList());
這段代碼首先把每行的單詞用 flatMap 整理到新的 Stream,然后保留長(zhǎng)度不為 0 的,就是整篇文章中的全部單詞了。
forEach
forEach 方法接收一個(gè)lambda表達(dá)式,然后在每個(gè)元素上執(zhí)行該表達(dá)式。
打印姓名 (java8 和之前版本對(duì)比)
List<Person> personList = new LinkedList<>();
/*java8*/
personList.stream().
filter(p -> p.getGender() == 0).
forEach(System.out::println);
/*java8 之前*/
for (Person person : personList) {
if(person.getGender() == 0) {
System.out.println(person.getName());
}
}
對(duì)于一個(gè)人員集合遍歷,找出男性并打印??梢钥闯鰜?lái),forEach是為lambda而設(shè)計(jì)的,保持了最緊湊的風(fēng)格。而且lambda 表達(dá)式本身是可以重用的,非常方便。當(dāng)需要為多核系統(tǒng)優(yōu)化時(shí),可以parallelStream().forEach(),只是此時(shí)原有元素的次序沒(méi)法保證,并行的情況下將改變串行時(shí)操作的行為,此時(shí)forEach本身的實(shí)現(xiàn)不需要調(diào)整,而java8以前的for循環(huán)code可能需要加入額外的多線程邏輯。
但一般認(rèn)為,forEach和常規(guī)的for循環(huán)的差異不涉及到性能,他們僅僅是函數(shù)式風(fēng)格與傳統(tǒng)java風(fēng)格的差別。
另外一點(diǎn)需要注意,forEach是Terminal操作,因此它執(zhí)行后,Stream的元素就被“消費(fèi)”掉了,你無(wú)法對(duì)一個(gè)Stream進(jìn)行兩次Terminal運(yùn)算。例如下面的代碼是錯(cuò)誤的。
stream.forEach(element -> doOneThing(element));
stream.forEach(element -> doAnotherThing(element));
相反,具有相似功能的intermediate操作peek可以達(dá)到上述目的,如下是出現(xiàn)在該API javadoc上的一個(gè)示例。
peek對(duì)每個(gè)元素執(zhí)行操作并返回一個(gè)新的Stream
Stream.of("one","two","three","four","five").
filter(x -> x.length() >3).
peek(x -> System.out.println("filtered value:"+x)).
map(String::toUpperCase).
peek(x -> System.out.println("Mapped value:"+x)).
collect(Collectors.toList());
forEach 不能修改自己包含的本地變量值,也不能用 break/return 之類的關(guān)鍵字提前結(jié)束循環(huán)。
findFirst
這是一個(gè)terminal 兼short-circuiting操作,他總是返回Stream的第一個(gè)元素,或者空。
這里的比較重點(diǎn)是它的返回值類型:Optional。這也是一個(gè)模仿Scala語(yǔ)言中的概念,作為一個(gè)容器,他可能含有某值,或者不包含。使用它的目的是盡可能避免 NullPointException。
Optional的兩個(gè)用例
public static void print(String text){
/*Java8*/
Optional.ofNullable(text).ifPresent(System.out::println);
/*Java8 之前版本*/
if(text != null) {
System.out.println(text);
}
}
public static int getLength(String text) {
/*Java8*/
return Optional.ofNullable(text).map(String::length).orElse(-1);
/*Java8之前*/
// return text != null ? text.length() : -1;
}
String strA = " abcd ",strB = null;
print(strA);
print("");
print(strB);
System.out.println(getLength(strA));
System.out.println(getLength(""));
System.out.println(getLength(strB));
在更復(fù)雜的if(xx != null )的情況中,使用Optional代碼的可讀性更好,而且它提供的是編譯時(shí)檢查,能極大地降低NullPointException 對(duì)程序的影響,或者迫使程序員更早的在編碼階段處理空值的問(wèn)題,而不是留到運(yùn)行時(shí)在發(fā)現(xiàn)和調(diào)試。
Stream中的findAny、max/min、reduce等方法等返回Optional值。還有例如IntStream.average() 返回OptionalDouble等等。
reduce
這個(gè)方法的主要作用是把Stream元素組合起來(lái)。它提供了一個(gè)起始值(種子),然后依照運(yùn)算規(guī)則,和前面的Stream的第一個(gè)、第二個(gè)、第n個(gè)元素組合。從這個(gè)意義上說(shuō),字符串拼接、數(shù)值的sum、min、max、average都是特殊的reduce。例如Stream的sum就相當(dāng)于
Integer sum = Stream.of(1,2,3,4,5).reduce(0,(a,b) -> a+b); 或
Integer sum1 = Stream.of(1,2,3,4,5).reduce(0,Integer::sum);
也有沒(méi)有起始值的情況,這時(shí)會(huì)把Stream的前面兩個(gè)元素組合起來(lái),返回的是Optional。
reduce的用例
/*字符串連接,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 無(wú)起始值*/
sumValue = Stream.of(1,2,3,4).reduce(Integer::sum).get();
/*過(guò)濾,字符串連接,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è)示例沒(méi)有起始值的reduce(),由于可能沒(méi)有足夠的元素,返回的是Optional,請(qǐng)留意這個(gè)區(qū)別。
limit/skip
limit返回Stream的前面n個(gè)元素;skip則是扔掉前n個(gè)元素(它是由一個(gè)叫subStream的方法改名而來(lái))。
limit和skip對(duì)運(yùn)行次數(shù)的影響
/*limit和skip對(duì)運(yùn)行次數(shù)的影響*/
public static void testLimitAndSkip() {
List<Person> persons = new LinkedList<>();
for(int i = 0 ;i <10000 ;i++) {
Person person = new Person();
person.setGender(1);
person.setName("name"+i);
persons.add(person);
}
persons.stream().map(Person::getName).
limit(10).skip(3).collect(Collectors.toList()).
forEach(System.out::println);
輸出結(jié)果為:

這是一個(gè)由10000個(gè)元素的Stream 但在short-circuting操作limit和skip的作用下,管道中map操作指定的getName方法的執(zhí)行次數(shù)為limit所限定的10次,而最終返回結(jié)果在跳過(guò)前三個(gè)元素后,只有7個(gè)返回。
有一種情況是limit/skip無(wú)法達(dá)到short-circuiting目的的,就是把它們放在Stream的排序操作后,原因跟sorted這個(gè)intermediate操作有關(guān),此時(shí)系統(tǒng)并不知道Stream破愛(ài)徐厚的次序如何,所以sorted中的操作看上去就像完全沒(méi)有被limit或者skip一樣。即sorted之后加了limit 其實(shí)還是所有數(shù)據(jù)都會(huì)遍歷的。
limit 和 skip 對(duì) sorted 后的運(yùn)行次數(shù)無(wú)影響
List<Person> list = persons.stream().
sorted(Comparator.comparing(Person::getName)).
limit(2).collect(Collectors.toList());
System.out.println(JSONObject.toJSONString(list));
最后有一點(diǎn)需要注意的是,對(duì)一個(gè)parallel的Stream管道來(lái)說(shuō)沒(méi)如果其元素是有序的,那么limit操作,那么limit的操作的成本會(huì)比較大,因?yàn)樗姆祷貙?duì)象必須是前n個(gè)也有一樣次序的元素。取而代之的策略是取消元素間的次序,或者不要用parallel Stream。
sorted
對(duì)Stream 的排序通過(guò)sorted進(jìn)行,它比數(shù)組的排序更強(qiáng)大之處在于你可以首先對(duì)Stream進(jìn)行各類map、filter、limit、skip甚至distinct來(lái)減少元素?cái)?shù)量后,在排序,這能幫助程序明顯縮短執(zhí)行時(shí)間。
persons.stream().
sorted(Comparator.comparing(Person::getName)).
limit(2).collect(Collectors.toList());
min/max/distinct
min和max的功能也可以通過(guò)對(duì)Stream元素先排序,再findFirest來(lái)實(shí)現(xiàn),但前者性能會(huì)更好,為O(n) 而sorted的成本是O(nlogn).同時(shí)它們作為特殊的reduce方法被獨(dú)立出來(lái)也是因?yàn)榍笞畲笾底钚≈凳呛艹R?jiàn)的操作。
找出最長(zhǎng)的一行長(zhǎng)度
BufferedReader br = new BufferedReader(new FileReader("D:\\data.log"));
int longest = br.lines().mapToInt(String::length).max().orElse(0);
br.close();
System.out.println(longest);
找出全文的單詞,轉(zhuǎn)小寫(xiě),并排序
List<String> words = br.lines().
flatMap(line ->Stream.of(line.split(" "))).
filter(word -> word.length() > 0).map(String::toLowerCase).
distinct().sorted().collect(Collectors.toList());
br.close();
System.out.println(words);
Match
Stream 有三個(gè)match方法,從語(yǔ)義上說(shuō):
- allMatch:Stream中全部元素符合傳入的predicate,返回true
- anyMatch:Stream中只要有一個(gè)元素符合傳入的predicate,返回true
- noneMatch:Stream中沒(méi)有一個(gè)元素符合傳入的predicate,返回true
它們都不需要遍歷全部元素。例如allMatch只要一個(gè)元素不滿足條件。就skip剩下的所有元素,返回false。
檢測(cè)名稱編號(hào)大于3的
/*檢測(cè)名稱編號(hào)大于3的*/
List<Person> persons = new LinkedList<>();
persons.add(new Person(0,"name1"));
persons.add(new Person(0,"name2"));
persons.add(new Person(0,"name3"));
persons.add(new Person(0,"name4"));
persons.add(new Person(0,"name5"));
boolean isAllMoreThan3 = persons.stream().
allMatch(p -> p.getName().compareTo("name3") > 0);
System.out.println("All more than 3 ? " + isAllMoreThan3);
boolean isAnyMoreThan3 = persons.stream().
anyMatch(p -> p.getName().compareTo("name3") >0);
System.out.println("Is there any more than 3? " + isAnyMoreThan3);
進(jìn)階:字節(jié)生成流
Stream.generate
通過(guò)實(shí)現(xiàn)Supplier接口,你可以自己來(lái)控制流的生成,這種情形通常用于隨機(jī)數(shù)、常量的Stream,或者需要前后元素間的維持著某種狀態(tài)信息的Stream。把Supplier實(shí)例傳遞給Strteam.generate()生成Stream,默認(rèn)都是串行(相對(duì)于parallel而言)但無(wú)序的(相對(duì)于ordered而言)。由于它是無(wú)限的,在管道中,必須利用limit之類的操作限制Stream大小。
使用Supplier生成10個(gè)隨機(jī)整數(shù)
Random seed = new Random();
Supplier<Integer> random = seed::nextInt;
Stream.generate(random).limit(10).
forEach(System.out::println);
/*Another way*/
IntStream.generate(() -> (int)(System.nanoTime()%100)).
limit(10).forEach(System.out::println);
Stream.generate()還接受自己實(shí)現(xiàn)的Supplier 。例如在構(gòu)造海量測(cè)試數(shù)據(jù)的時(shí)候,用某種自動(dòng)的規(guī)則給每一個(gè)變量賦值;或者依據(jù)公式計(jì)算Stream的每個(gè)元素值。
自實(shí)現(xiàn)Supplier
Stream.generate(new PersonSupplier()).limit(10).
forEach(p -> System.out.println(p.getName()));
private class PersonSupplier implements Supplier<Person>{
private int index = 0;
private Random random = new Random();
@Override
public Person get() {
return new Person(index++,"name"+random.nextInt(100));
}
}
輸出結(jié)果:

Stream.iterate
iterate跟reduce操作很像,接受一個(gè)種子值,和一個(gè)UnaryOperator(例如f)。然后種子值稱為Stream的第一個(gè)元素,f(seed)為第二個(gè),f(f(seed))第三個(gè),以此類推。
使用iterate生成一個(gè)等差數(shù)列
Stream.iterate(0, m -> m+3).limit(10).forEach(System.out::println);
與Stream.generate相仿,在iterate時(shí)候管道必須有l(wèi)imit這樣的操作來(lái)限制Stream的大小。
進(jìn)階:用Collectors來(lái)進(jìn)行reduction操作
java.util.stream.Collectors類主要作用就是輔助進(jìn)行各類有用的reduction操作,例如轉(zhuǎn)變輸出為Collection,把Stream元素進(jìn)行歸組。
groupingBy/partitioningBy
按照姓名歸組
Map<String,List<Person>> personGroups = Stream.generate(new PersonSupplier()).
limit(100).collect(Collectors.groupingBy(Person::getName));
上面的代碼,首先生成100人的信息,然后按照姓名歸組,相同姓名的人放到同一個(gè)list(由于姓名都是不同的,所以每個(gè)person會(huì)存在一個(gè)key中)。
按照指定條件歸組,將name10作為分界
Map<Boolean,List<Person>> children = Stream.generate(new PersonSupplier()).
limit(20).collect(Collectors.partitioningBy(person ->
person.getName().compareTo("name10") > 0));
在使用條件 person.getName().compareTo("name10") > 0進(jìn)行分組后可以看到,小于name10的十一組,大于name10的是另外一組。partitioningBy其實(shí)是一種特殊的groupingBy,它依照條件測(cè)試的是否兩種結(jié)果來(lái)構(gòu)造返回的數(shù)據(jù)結(jié)構(gòu),get(true)和get(false)能即為全部的元素對(duì)象。
結(jié)束語(yǔ)
總之Stream的特性可以歸納為:
- 不是數(shù)據(jù)結(jié)構(gòu)
- 他沒(méi)有內(nèi)部存儲(chǔ),它只是用操作管道從source(數(shù)據(jù)結(jié)構(gòu),數(shù)組、generator function、IO channel)抓取數(shù)據(jù)。
- 它也絕不修改自己所封裝的底層數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)。例如Stream的filter操作會(huì)產(chǎn)生一個(gè)不包含被過(guò)濾元素的心Stream,而不是從source刪除那些元素。
- 所有的Stream的操作必須以lambda表達(dá)式為參數(shù)
- 不支持索引訪問(wèn)
- 可以請(qǐng)求第一個(gè)元素,但無(wú)法請(qǐng)求第二個(gè)第三個(gè)或最后一個(gè)元素。
- 很容易生成數(shù)組或者List
- 惰性化
- 很多Stream操作是向后延遲的,一直到它弄清楚了最后需要多少數(shù)據(jù)才會(huì)開(kāi)始。
- Intermediate操作永遠(yuǎn)是惰性化的。
- 并行能力
- 當(dāng)一個(gè)Stream是并行化的,就不需要寫(xiě)多線程代碼,所有對(duì)它的操作會(huì)自動(dòng)并行進(jìn)行。
- 可以是無(wú)限的。集合有固定大小,Stream則不必。limit(n)和findFirst()這類short-circuting操作可以對(duì)無(wú)限的Stream進(jìn)行運(yùn)算并很快完成。