Java8 Stream,過分絲滑!
文章來源:https://c1n.cn/5n6fT
目錄
-
Stream 流
-
Stream 流的使用
Stream 流
Stream 流是什麼,為什麼要用它?
-
S tream是 Java8 新引入的一個包( java.util.stream),它讓我們能用宣告式的方式處理資料(集合、陣列等)。
-
Stream流式處理相較於傳統方法簡潔高效,也便於進行併發程式設計。
Stream 是 Java8 的一大亮點,是對容器物件功能的增強,它專注於對容器物件進行各種非常便利、高效的聚合操作(aggregate operation)或者大批量資料操作。
Stream 使用一種類似用 SQL 語句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
Stream API 藉助於同樣新出現的 Lambda 表示式,極大的提高程式設計效率和程式可讀性。
同時,它提供序列和並行兩種模式進行匯聚操作,併發模式能夠充分利用多核處理器的優勢,使用 fork/join 並行方式來拆分任務和加速處理過程。
所以說,Java8 中首次出現的 java.util.stream 是一個函式式語言+多核時代綜合影響的產物。
Stream 流的使用
| 簡單綜合案例
問題和需求:在一個字串集合中找出以“阿”開頭的長度為 3 的字串並列印。
傳統方法:
import java.util.ArrayList; import java.util.List; public class Demo02NormalFilter { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("阿拉貢"); list.add("阿爾玟"); list.add("埃爾隆德"); list.add("凱蘭崔爾"); list.add("瑟蘭督伊"); List<String> zhangList = new ArrayList<>(); for (String name : list) { if (name.startsWith("阿")) { zhangList.add(name); } } List<String> shortList = new ArrayList<>(); for (String name : zhangList) { if (name.length() == 3) { shortList.add(name); } } for (String name : shortList) { System.out.println(name); } } }
這裡我們可以看到傳統的方法中含有三個迴圈,每一個作用不同:
-
首先篩選所有姓“阿”的人
-
然後篩選名字有三個字的人
-
最後進行對結果進行列印輸出
這樣的處理過程程式碼冗長,導致程式碼可讀性較差,效率也比較低。而使用 Stream 來進行處理就能使程式碼優雅地多。
Stream 流式處理方法:
import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class Demo02NormalFilter { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("阿拉貢"); list.add("阿爾玟"); list.add("埃爾隆德"); list.add("凱蘭崔爾"); list.add("瑟蘭督伊"); list.stream() .filter(s ‐> s.startsWith("阿")) .filter(s ‐> s.length() == 3) .forEach(System.out::println); } }
利用 Stream 流中的方法再結合函式式介面和 Lambda 表示式,我們的程式碼就能變得格外簡潔明瞭。
| 獲取流
①根據 Collection 獲取流
首先,java.util.Collection 介面中加入了 default 方法 stream 用來獲取流,所以其所有實現類均可獲取流。
import java.util.*; import java.util.stream.Stream; public class Demo04GetStream { public static void main(String[] args) { List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); Set<String> set = new HashSet<>(); Stream<String> stream2 = set.stream(); Vector<String> vector = new Vector<>(); Stream<String> stream3 = vector.stream(); } }
②根據 Map 獲取流
java.util.Map 介面不是 Collection 的子介面,且其 K-V 資料結構不符合流元素的單一特徵,所以獲取對應的流需要分 key、value 或 entry 等情況:
import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; public class Demo05GetStream { public static void main(String[] args) { Map<String, String> map = new HashMap<>(); //Stream流的轉化需要單列資料,那麼我們就先把map裡面的資料變成單列的再轉化為流 Stream<String> keyStream = map.keySet().stream(); Stream<String> valueStream = map.values().stream(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); } }
③根據陣列獲取流
如果使用的不是集合或對映而是陣列,由於陣列物件不可能新增預設方法,所以 Stream 介面中提供了靜態方法 of ,使用很簡單:
import java.util.stream.Stream; public class Demo06GetStream { public static void main(String[] args) { String[] array = { "阿拉貢", "阿爾玟", "埃爾隆德", "凱蘭崔爾","瑟蘭督伊" }; Stream<String> stream = Stream.of(array); } }
| 常用方法
流模型的操作很豐富,這裡介紹一些常用的 API。
這些方法可以被分成兩種:
-
延遲方法: 返回值型別仍然是 Stream 介面自身型別的方法,因此支援鏈式呼叫。(除了終結方法外,其餘方法均為延遲方法)
-
終結方法: 返回值型別不再是 Stream 介面自身型別的方法,因此不再支援類似 StringBuilder 那樣的鏈式呼叫。( 如果想知道更多 方法,建議自行參考 API 文件 )
接下來我會介紹兩個終結方法 count 和 forEach 方法。
①逐一處理:forEach
雖然方法名字叫 forEach ,但是與 for 迴圈中的“for-each”語句是不一樣的,該方法接收一個 Consumer 介面函式,會將每一個流元素交給該函式進行處理。
方法簽名:
void forEach(Consumer<? super T> action);
基本使用:
import java.util.stream.Stream; public class Demo12StreamForEach { public static void main(String[] args) { Stream<String> stream = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德"); stream.forEach(name‐> System.out.println(name)); } }
篩選:filter
可以通過 filter 方法將一個流轉換成另一個子集流。該方法接收一個 Predicate 函式式介面引數(可以是一個 Lambda 或方法引用)作為篩選條件。
方法簽名:
Stream<T> filter(Predicate<? super T> predicate);
基本使用:
import java.util.stream.Stream; public class Demo07StreamFilter { public static void main(String[] args) { Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德"); Stream<String> result = original.filter(s ‐> s.startsWith("阿")); } }
在這裡通過 Lambda 表示式來指定了篩選的條件:必須以“阿”開頭。
對映:map
如果需要將流中的元素對映到另一個流中,可以使用 map 方法。該介面需要一個 Function 函式式介面引數,可以將當前流中的 T 型別資料轉換為另一種 R 型別的流。
方法簽名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
基本使用:
import java.util.stream.Stream; public class Demo08StreamMap { public static void main(String[] args) { Stream<String> original = Stream.of("10", "12", "18"); Stream<Integer> result = original.map(str‐>Integer.parseInt(str)); } }
這段程式碼中, map 方法的引數通過方法引用,將字串型別轉換成為了 int 型別(並自動裝箱為 Integer 類物件)。
統計個數:count
正如舊集合 Collection 當中的 size 方法一樣,流提供 count 方法來數一數其中的元素個數。該方法返回一個 long 值代表元素個數(不再像舊集合那樣是 int 值)。
方法簽名:
long count();
基本使用:
import java.util.stream.Stream; public class Demo09StreamCount { public static void main(String[] args) { Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德"); Stream<String> result = original.filter(s ‐> s.startsWith("阿")); System.out.println(result.count()); // 2 } }
取用前幾個:limit
limit 方法可以對流進行擷取,只取用前 n 個。引數是一個 long 型,如果集合當前長度大於引數則進行擷取;否則不進行操作。
方法簽名:
Stream<T> limit(long maxSize);
基本使用:
import java.util.stream.Stream; public class Demo10StreamLimit { public static void main(String[] args) { Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德"); Stream<String> result = original.limit(2); System.out.println(result.count()); // 2 } }
跳過前幾個:skip
如果希望跳過前幾個元素,可以使用 skip 方法獲取一個擷取之後的新流。如果流的當前長度大於 n,則跳過前 n 個;否則將會得到一個長度為 0 的空流。
方法簽名:
Stream<T> skip(long n);
基本使用:
import java.util.stream.Stream; public class Demo11StreamSkip { public static void main(String[] args) { Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德"); Stream<String> result = original.skip(2); System.out.println(result.count()); // 1 } }
②組合:concat
如果有兩個流,希望合併成為一個流,那麼可以使用 Stream 介面的靜態方法 concat。
這是一個靜態方法,與 java.lang.String 當中的 concat 方法是不同的。
方法簽名:
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T>b)
基本使用:
import java.util.stream.Stream; public class Demo12StreamConcat { public static void main(String[] args) { Stream<String> streamA = Stream.of("阿拉貢"); Stream<String> streamB = Stream.of("阿爾玟"); Stream<String> result = Stream.concat(streamA, streamB); } }
------------- END -------------
掃碼 免費 獲取 600+頁 石杉老師原創精品文章彙總PDF
原創技術文章彙總

點個 在看 你最好看

- Controller層程式碼這麼寫,簡潔又優雅!
- 同事把RabbitMQ講透了,佩服!
- 這是我見過寫得最爛的Controller層程式碼...
- BigDecimal,切記別再用錯了!
- 替代SpringCloud,Istio好用到爆!
- 吐血整理:一份不可多得的架構師圖譜!
- Java8 Stream,過分絲滑!
- 為什麼不建議使用ON DUPLICATE KEY UPDATE?
- 改造BeanUtils,優雅實現List資料拷貝
- 讓人上癮的新一代開發神器,徹底告別Controller、Service、Dao等方法
- 40個SpringBoot常用註解:讓生產力爆表!
- 令人頭疼的分散式事務,1次講明白!
- 分散式鎖的 3 種實現方案!(面試必問)
- 比MyBatis快100倍,天生支援聯表!
- Kafka、Netty都在用的Unsafe類,到底有多神?
- 同事多執行緒使用不當導致OOM,被我懟了一頓
- 自從上了Prometheus,睡覺真香!
- 為什麼不建議你使用SELECT * ?
- 你見過哪些目瞪口呆的 Java 程式碼技巧?
- 聊聊網際網路行業對35歲碼農的偏見,以及大齡碼農的破局之道