Stream是Java 8中引入的一個新的抽象層。
Stream 是對集合(Collection)對象功能的加強,它專一於對集合對象進行各類很是便利、高效的聚合操做(aggregate operation),或者大批量數據操做 (bulk data operation)。
Stream API 藉助於一樣新出現的 Lambda 表達式,極大的提升編程效率和程序可讀性。同時它提供串行和並行兩種模式進行匯聚操做,併發模式可以充分利用多核處理器的優點 Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。獲取一個數據源(source)→ 數據轉換→執行操做獲取想要的結果,每次轉換原有 Stream 對象不改變,返回一個新的 Stream 對象(能夠有多 使用流,能夠以相似於SQL語句的聲明方式來處理數據。例如,如下SQL語句 SELECT max(salary),employee_id,employee_name FROM Employee 上述SQL表達式自動返回最高受薪僱員的詳細信息,而不須要客戶端作任何事情。在Java中使用集合框架,開發人員必須使用循環並進行重複檢查。 另外一個問題是效率;因爲如今的電腦基本都是多核處理器,所以Java開發人員能夠編寫並行代碼處理,可是每每會出錯 爲了解決這些問題,Java 8引入了流的概念,讓開發人員以聲明方式處理數據,並利用多核架構,而無需爲其編寫任何特定的代碼。 (代碼簡潔+多核處理) stream並非某種數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。獲取一個數據源(source)→ 數據轉換→執行操做獲取想要的結果,每次轉換原有 Stream 對象不改變,返回一個新的 Stream 對象(能夠有屢次轉換),這就容許對其操做能夠像鏈條同樣排列,變成一個管道,它只是數據源的一種視圖。這裏的數據源能夠是一個數組,Java容器或I/O channel等。正因如此要獲得一個stream一般不會手動建立,而是調用對應的工具方法,好比:
- 調用
Collection.stream()
或者Collection.parallelStream()
方法
- 調用
Arrays.stream(T[] array)
方法
經常使用的四種stream接口繼承關係以下圖:
圖中4種stream接口繼承自
BaseStream
,其中
IntStream, LongStream, DoubleStream
對應三種基本類型(
int, long, double
,注意不是包裝類型),
Stream
對應全部剩餘類型的stream視圖。爲不一樣數據類型設置不一樣stream接口,能夠1.提升性能,2.增長特定接口函數
你可能會奇怪爲何不把
IntStream
等設計成
Stream
的子接口?畢竟這接口中的方法名大部分是同樣的。答案是這些方法的名字雖然相同,可是返回類型不一樣,若是設計成父子接口關係,這些方法將不能共存,由於Java不容許只有返回類型不一樣的方法重載。 雖然大部分狀況下stream是容器調用
Collection.stream()
方法獲得的,但stream和collections有如下不一樣:
- 無存儲。stream不是一種數據結構,它只是某種數據源的一個視圖,數據源能夠是一個數組,Java容器或I/O channel等。
- 爲函數式編程而生。對stream的任何修改都不會修改背後的數據源,好比對stream執行過濾操做並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream。(從新生成+副本)
- 惰式執行。stream上的操做並不會當即執行,只有等到用戶真正須要結果的時候纔會執行。(中間操做和結束操做)
- 可消費性。stream只能被「消費」一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須從新生成。
count = strings.stream().filter(string -> string.isEmpty()).count();//filter裏面是定義好的規則 其參數就是predicate
System.out.println("Empty Strings: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("Strings of length 3: " + count);
每次使用都是從新調用stream() 對stream的操做分爲爲兩類,中間操做(intermediate operations)和結束操做(terminal operations),兩者特色是:
- 中間操做老是會惰式執行,調用中間操做只會生成一個標記了該操做的新stream,僅此而已。
- 結束操做會觸發實際計算,計算髮生時會把全部中間操做積攢的操做以pipeline的方式執行,這樣能夠減小迭代次數。計算完成以後stream就會失效。下表彙總了
Stream
接口的部分常見方法:
操做類型 |
接口方法 |
中間操做 |
concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered() |
結束操做 |
allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray() |
爲何不在集合類實現這些操做,而是定義了全新的Stream API?Oracle官方給出了幾個重要緣由: 一是集合類持有的全部元素都是存儲在內存中的,很是巨大的集合類會佔用大量的內存,而Stream的元素倒是在訪問的時候才被計算出來,這種「延遲計算」的特性有點相似Clojure的lazy-seq,佔用內存不多。 二是集合類的迭代邏輯是調用者負責,一般是
for
循環,而Stream的迭代是隱含在對Stream的各類操做中,例如
map()
。 對於基本數值型,目前有三種對應的包裝類型 Stream:IntStream、LongStream、DoubleStream。 關鍵字: filter:是一箇中間操做,接受一個predicate接口類型的變量,並將全部流對象中的元素進行過濾。filter(s -> s.getState()==State.pay) map:是一個對於流對象的中間操做,經過給定的方法,它可以把流對象中的每個元素對應到另一個對象上。map(s -> s.getPlanNo()) / map(s -> Plan::planNo) / 價格變成 10倍 map(s -> s.getPrice().multiply(BigDecimal.valueOf(10))) reduce:把 Stream 元素組合起來。它提供一個起始值(種子),而後依照運算規則(BinaryOperator),返回單個的結果值,而且reduce操做每處理一個元素老是建立一個新值 BigDecimal total = stream().reduce(BigDecimal.zero, (a,b) -> a.add(b)); 或 BigDecimal total = stream().reduce(BigDecimal.ZERO, BigDecimal::add) limit : 返回 Stream 的前面 n 個元素;skip 則是扔掉前 n 個元素 sorted: 一箇中間操做,可以返回一個排過序的流對象的視圖。流對象中的元素會默認按照天然順序進行排序,除非你本身指定一個Comparator接口來改變排序規則. collect: 修改現存的值 Collectors 類的主要做用就是輔助進行各種有用的 reduction 操做 groupingBy 按規則分組:stream().collect(Collectors.groupingBy(p->p.getState())) partitioningBy 是一種特殊的 groupingBy,它依照條件測試的是否兩種結果來構造返回的數據結構,get(true) 和 get(false) 能即爲所有的元素對象。 Stream 有三個 match 方法,從語義上說: allMatch:Stream 中所有元素符合傳入的 predicate,返回 true anyMatch:Stream 中只要有一個元素符合傳入的 predicate,返回 true noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true