繼上次學習過Java8中的非常重要的Lambda表達式之后,接下來就要學習另一個也比較重要的知識啦,也就如標題所示:Stream,而它的學習是完全依賴于之前學習的Lambda表達式。
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數據。
Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
Stream API可以極大提高Java程序員的生產力,讓程序員寫出高效率、干凈、簡潔的代碼。
這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 并且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的結果。
+--------------------+ +------+ +------+ +---+ +-------+ | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect| +--------------------+ +------+ +------+ +---+ +-------+
以上的流程轉換為 Java 代碼為:
class Apple{ private int weigth; private String color; public int getWeigth() { return weigth; } public void setWeigth(int weigth) { this.weigth = weigth; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public Apple(int weigth, String color) { this.weigth = weigth; this.color = color; } } @Test public void testStream(){ List<Apple> transactionsIds = new ArrayList<>(); transactionsIds.add(new Apple(1,"紅色")); transactionsIds.add(new Apple(2,"綠色")); transactionsIds.add(new Apple(3,"綠色")); transactionsIds.add(new Apple(2,"綠色")); int weight= transactionsIds.stream() .filter(app ->app.getColor().equals("綠色")) .sorted(( app1, app2)->app2.getWeigth()-app1.getWeigth()) .mapToInt(Apple::getWeigth) .sum(); System.out.println(weight); }
以上代碼的寫法可讀性比較高,幾乎一眼能看出做了哪些操作。
Stream(流)是一個來自數據源的元素隊列并支持聚合操作
和以前的Collection操作不同, Stream操作還有兩個基礎的特征:
在 Java 8 中, 集合接口有兩個方法來生成流:
stream() ? 為集合創建串行流。
parallelStream() ? 為集合創建并行流。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
Stream 提供了新的方法 'forEach' 來迭代流中的每個數據。以下代碼片段使用 forEach 輸出了10個隨機數:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
map 方法用于映射每個元素到對應的結果,以下代碼片段使用 map 輸出了元素對應的平方數:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 獲取對應的平方數
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter 方法用于通過設置的條件過濾出元素。以下代碼片段使用 filter 方法過濾出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 獲取空字符串的數量
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit 方法用于獲取指定數量的流。 以下代碼片段使用 limit 方法打印出 10 條數據:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
sorted 方法用于對流進行排序。以下代碼片段使用 sorted 方法對輸出的 10 個隨機數進行排序:
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
parallelStream 是流并行處理程序的代替方法。以下實例我們使用 parallelStream 來輸出空字符串的數量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 獲取空字符串的數量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
我們可以很容易的在順序運行和并行直接切換。
Collectors 類實現了很多歸約操作,例如將流轉換成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("篩選列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
另外,一些產生統計結果的收集器也非常有用。它們主要用于int、double、long等基本類型上,它們可以用來產生類似如下的統計結果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的數 : " + stats.getMax()); System.out.println("列表中最小的數 : " + stats.getMin()); System.out.println("所有數之和 : " + stats.getSum()); System.out.println("平均數 : " + stats.getAverage());
一、stream支持并行,這個怎么驗證呢?可以通過下面兩張圖可知


二、stream到底如何工作的?
下圖很清晰的展示了stream的工作方式,并且是一次性的,即操作過的stream無法再次被操作。

三、stream有啥好處?
下圖可以說明stream的優勢,很明顯,就是速度要快,因為stream只操作符合條件的數據,有短路的特點。

四、stream的迭代方式變化,也就是上文提及的由以前的“外部迭代”改成了“內部迭代”

五、stream的方法分為兩種,一種是Intermediate Operations(中間操作),一種是terminal operations(終止操作),這個應該好理解,繼續操作的命令就是中間操作。這里為什么要區分這兩種操作的,因為stream操作是屬于延遲操作,也就是說這段鏈式代碼必須遇到terminal operations才會去執行,否則是不會有任何效果,這個是容易踩得坑。

這里大致列一下,其實每個方法上大神都有說明是什么操作,另外我們根據返回類型也能確定是屬于什么操作。


六、stream的中間操作是不停的for循環嗎?
對于一個流可能有若干個中間操作,對于這些操作并非降低了整體的執行性能,反而會有提升,比如說增加了三個中間操作,可能感受會有三次循環,但是實際上最終只會循環一次,可以這么簡單的理解,以于這些中間操作的行為都是存放于Stream中的一個容器當中,一旦遇到終止操作時,則會將這些行為按照咱們寫的順序逐個的應用到集合當中的每一個元素上,所以說性能上不會有任何影響。
七、stream會修改源數據嗎?
并不會修改底層的數據源,集合可以作為流的底層數據源
參考http://www.rzrgm.cn/webor2006/p/7887244.html,https://www.runoob.com/java/java8-streams.html