摘要:另外,像這樣生成值的操作和這樣產生副作用的操作都是天然急性求值,因為它們必須要產生具體的結果。這樣可以大大減少維持中間結果所帶來的開銷。現在我們需要篩選出含有平米以上房源的小區,并按照小區名排序。
轉載請注明出處:https://zhuanlan.zhihu.com/p/20540202
Stream作為Java8的新特性之一,他與Java IO包中的InputStream和OutputStream完全不是一個概念。Java8中的Stream是對集合功能的一種增強,主要用于對集合對象進行各種非常便利高效的聚合和大批量數據的操作。結合Lambda表達式可以極大的提高開發效率和代碼可讀性。
假設我們需要把一個集合中的所有形狀設置成紅色,那么我們可以這樣寫
for (Shape shape : shapes){ shape.setColor(RED) }
如果使用Java8擴展后的集合框架則可以這樣寫:
shapes.foreach(s -> s.setColor(RED));
__第一種__寫法我們叫外部迭代,for-each調用shapes的iterator()依次遍歷集合中的元素。這種外部迭代有一些問題:
for循環是串行的,而且必須按照集合中元素的順序依次進行;
集合框架無法對控制流進行優化,例如通過排序、并行、短路求值以及惰性求值改善性能。
上面這兩個問題我們會在后面的文章中逐步解答。
__第二種__寫法我們叫內部迭代,兩段代碼雖然看起來只是語法上的區別,但實際上他們內部的區別其實非常大。用戶把對操作的控制權交還給類庫,從而允許類庫進行各種各樣的優化(例如亂序執行、惰性求值和并行等等)??偟膩碚f,內部迭代使得外部迭代中不可能實現的優化成為可能。
外部迭代同時承擔了做什么(把形狀設為紅色)和怎么做(得到Iterator實例然后依次遍歷),而內部迭代只負責做什么,而把怎么做留給類庫。這樣代碼會變得更加清晰,而集合類庫則可以在內部進行各種優化。
一、什么是StreamStream不是集合元素,它也不是數據結構、不能保存數據,它更像一個更高級的Interator。Stream提供了強大的數據集合操作功能,并被深入整合到現有的集合類和其它的JDK類型中。流的操作可以被組合成流水線(Pipeline)。拿前面的例子來說,如果我只想把藍色改成紅色:
shapes.stream() .filter(s -> s.getColor() == BLUE) .forEach(s -> s.setColor(RED));
在Collection上調用stream()會生成該集合元素的流,接下來filter()操作會產生只包含藍色形狀的流,最后,這些藍色形狀會被forEach操作設為紅色。
如果我們想把藍色的形狀提取到新的List里,則可以:
Listblue = shapes.stream() .filter(s -> s.getColor() == BLUE) .collect(Collectors.toList());
collect()操作會把其接收的元素聚集到一起(這里是List),collect()方法的參數則被用來指定如何進行聚集操作。在這里我們使用toList()以把元素輸出到List中。
如果每個形狀都被保存在Box里,然后我們想知道哪個盒子至少包含一個藍色形狀,我們可以這么寫:
SethasBlueShape = shapes.stream() .filter(s -> s.getColor() == BLUE) .map(s -> s.getContainingBox()) .collect(Collectors.toSet());
map()操作通過映射函數(這里的映射函數接收一個形狀,然后返回包含它的盒子)對輸入流里面的元素進行依次轉換,然后產生新流。
如果我們需要得到藍色物體的總重量,我們可以這樣表達:
int sum = shapes.stream() .filter(s -> s.getColor() == BLUE) .mapToInt(s -> s.getWeight()) .sum();二、Stream vs Collection
流(Stream)和集合(Collection)的區別:
Collection主要用來對元素進行管理和訪問;
Stream并不支持對其元素進行直接操作和直接訪問,而只支持通過聲明式操作在其之上進行運算后得到結果;
Stream不存儲值
對Stream的操作會產生一個結果,但是Stream并不會改變數據源;
大多數Stream的操作(filter,map,sort等)都是以惰性的方式實現的。這使得我們可以使用一次遍歷完成整個流水線操作,并可以用短路操作提供更高效的實現。
三、惰性求值 vs 急性求值filter()和map()這樣的操作既可以被急性求值(以filter()為例,急性求值需要在方法返回前完成對所有元素的過濾),也可以被惰性求值(用Stream代表過濾結果,當且僅當需要時才進行過濾操作)在實際中進行惰性運算可以帶來很多好處。比如說,如果我們進行惰性過濾,我們就可以把過濾和流水線里的其它操作混合在一起,從而不需要對數據進行多遍遍歷。相類似的,如果我們在一個大型集合里搜索第一個滿足某個條件的元素,我們可以在找到后直接停止,而不是繼續處理整個集合。(這一點對無限數據源是很重要,惰性求值對于有限數據源起到的是優化作用,但對無限數據源起到的是決定作用,沒有惰性求值,對無限數據源的操作將無法終止)
對于filter()和map()這樣的操作,我們很自然的會把它當成是惰性求值操作,不過它們是否真的是惰性取決于它們的具體實現。另外,像sum()這樣生成值的操作和forEach()這樣產生副作用的操作都是__天然急性求值__,因為它們必須要產生具體的結果。
我們拿下面這段代碼舉例:
int sum = shapes.stream() .filter(s -> s.getColor() == BLUE) .mapToInt(s -> s.getWeight()) .sum();
這里的filter()和map()都是惰性的,這就意味著在調用sum()之前不會從數據源中提取任何元素。在sum()操作之后才會把filter()、map()和sum()放在對數據源一次遍歷中。這樣可以大大減少維持中間結果所帶來的開銷。
四、舉個栗子?前面長篇大論的介紹概念實在太枯燥,為了方便大家理解我們用Streams API來實現一個具體的業務場景。
假設我們有一個房源庫項目,這個房源庫中有一系列的小區,每個小區都有小區名和房源列表,每套房子又有價格、面積等屬性。現在我們需要篩選出含有100平米以上房源的小區,并按照小區名排序。
我們先來看看不用Streams API如何實現:
Listresult = new ArrayList<>(); for (Community community : communities) { for (House house : community.houses) { if (house.area > 100) { result.add(community); break; } } } Collections.sort(result, new Comparator () { @Override public int compare(Community c1, Community c2) { return c1.name.compareTo(c2.name); } }); return result;
如果使用Streams API:
return communities.stream() .filter(c -> c.houses.stream().anyMatch(h -> h.area>100)) .sorted(Comparator.comparing(c -> c.name)) .collect(Collectors.toList());
如果大家喜歡這一系列的文章,歡迎關注我的知乎專欄、GitHub、簡書博客。
知乎專欄:https://zhuanlan.zhihu.com/baron
GitHub:https://github.com/BaronZ88
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66489.html
摘要:現在爸爸終于讓平臺支持了,這篇文章中便來和大家聊聊如何在項目中配置使用。要想在項目中使用的新特性,需要將你的升級到及以上版本,并采用新的編譯。 轉載請注明出處:https://zhuanlan.zhihu.com/p/23279894 前言 在過去的文章中我介紹過Java8的一些新特性,包括: Java8新特性第1章(Lambda表達式) Java8新特性第2章(接口默認方法) J...
摘要:一表達式匿名內部類最大的問題在于其冗余的語法,比如前面的中五行代碼僅有一行是在執行任務。總結基于詞法作用域的理念,表達式不可以掩蓋任何其所在上下文的局部變量。 轉載請注明出處:https://zhuanlan.zhihu.com/p/20540175 在介紹Lambda表達式之前,我們先來看只有單個方法的Interface(通常我們稱之為回調接口): public interface...
摘要:組合式異步編程最近這些年,兩種趨勢不斷地推動我們反思我們設計軟件的方式。第章中介紹的分支合并框架以及并行流是實現并行處理的寶貴工具它們將一個操作切分為多個子操作,在多個不同的核甚至是機器上并行地執行這些子操作。 CompletableFuture:組合式異步編程 最近這些年,兩種趨勢不斷地推動我們反思我們設計軟件的方式。第一種趨勢和應用運行的硬件平臺相關,第二種趨勢與應用程序的架構相關...
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
閱讀 1207·2021-09-03 10:44
閱讀 603·2019-08-30 13:13
閱讀 2796·2019-08-30 13:11
閱讀 1967·2019-08-30 12:59
閱讀 1034·2019-08-29 15:32
閱讀 1595·2019-08-29 15:25
閱讀 987·2019-08-29 12:24
閱讀 1277·2019-08-27 10:58