Collection 是程式中存放於內部記憶體的資料結構,而 Stream 是一種資料流 pipeline,可以依照需要使用這些內部記憶體的資料。
Stream 的 operation 有兩類:intermediate 或是 terminal,intermediate opertaion 會回傳 stream,故能夠以 chain 的方式依序串接多個 intermediate operation,針對資料進行運算。terminal operation 會於計算後,回傳某個類別的結果。
Stream operation 有循序跟平行化處理兩種,平行化的 operations 能夠在多個 thread 同時運算,能充分運用多核心處理器,加速運算的過程。
Stream 的資料來源是 java.util.Collection,例如 List, Set。Map 無法直接支援,但能分別對 keys, values 或 entries 建立 Stream。
建立 Stream
從 data collections 產生 Stream 的方法
private static void createStream() {
// Stream.of
System.out.println("Stream.of");
Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9);
stream.forEach(p -> System.out.print(p + " "));
System.out.println("");
// Stream.of(array)
System.out.println("Stream.of(array)");
Stream<Integer> stream2 = Stream.of( new Integer[]{1,2,3,4,5,6,7,8,9} );
stream2.forEach(p -> System.out.print(p + " "));
System.out.println("");
// List.stream()
System.out.println("List.stream()");
List<Integer> list = new ArrayList<Integer>();
for(int i = 1; i< 10; i++){
list.add(i);
}
Stream<Integer> stream3 = list.stream();
stream3.forEach(p -> System.out.print(p + " "));
System.out.println("");
// Stream.generate() or Stream.iterate()
// 透過 generator function 產生 Stream elements
// 以 limit() 限制 element 個數
System.out.println("Stream.generate()");
Stream<Integer> randomNumbers = Stream
.generate(() -> (new Random()).nextInt(100));
randomNumbers.limit(10).forEach(s -> System.out.print(s + " "));
System.out.println("");
// Stream of String chars or tokens
System.out.println("Stream of String chars");
IntStream stream5 = "12345_abcdefg".chars();
stream5.forEach(p -> System.out.print(p + " "));
System.out.println("");
System.out.println("Stream of tokens");
Stream<String> stream6 = Stream.of("A$B$C".split("\\$"));
stream6.forEach(p -> System.out.print(p + " "));
System.out.println("");
}
Stream Collector
透過 Collector 可將 Stream 裡面的 elements 再轉換為 Collection
private static void streamCollector() {
// List 轉換為 Stream
List<Integer> list = new ArrayList<Integer>();
for(int i = 1; i< 10; i++){
list.add(i);
}
Stream<Integer> stream = list.stream();
// 用 collect 再將 Stream 轉換為 List
System.out.println("collect");
List<Integer> evenNumbersList = stream.filter(i -> i%2 == 0)
.collect(Collectors.toList());
System.out.print(evenNumbersList);
System.out.println("");
// 用 collect 再將 Stream 轉換為 Array
System.out.println("toArray");
Stream<Integer> stream2 = list.stream();
Integer[] evenNumbersArr = stream2.filter(i -> i%2 == 0).toArray(Integer[]::new);
for (Integer i : evenNumbersArr) {
System.out.print(i + " ");
}
System.out.println("");
}
Operations
Intermediate Operations
method | description |
---|---|
filter() | 條件過濾 |
map() | 對每個 elements 執行某個運算 |
flatMap() | 將多個Stream連接成一個Stream |
distinct() | 刪除相同的 elements |
sorted() | 排序 |
peek() | 用在debug |
limit() | 限制回傳的 elements 數量 |
skip() | 略過前面幾個 elements |
java sample
private static void intermediateOperation() {
List<String> names = new ArrayList<>();
names.add("Allen");
names.add("George");
names.add("Anderson");
names.add("Danny");
// filter 條件過濾
System.out.println("filter");
names.stream().filter((s) -> s.startsWith("A"))
.forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("");
// map 對每個 elements 執行某個運算
System.out.println("map");
names.stream().filter((s) -> s.startsWith("A"))
.map(String::toUpperCase)
.forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("");
// flatMap 將多個Stream連接成一個Stream
List<String> names2 = new ArrayList<>();
names2.add("Jim");
System.out.println("flatMap");
List<String> flapMapStream = Stream.of(names, names2).flatMap(u -> u.stream()).collect(Collectors.toList());
System.out.println(flapMapStream);
System.out.println("");
System.out.println("");
// distinct
// 刪除相同的 elements
List<String> names3 = new ArrayList<>();
names3.add("Jim");
names3.add("Jimmy");
names3.add("Jim");
System.out.println("distinct");
names3.stream()
.distinct()
.forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("");
//sorted 排序
System.out.println("sorted");
names.stream()
.sorted()
.forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("");
// peek 用在debug
System.out.println("peek");
List res = names.stream().filter((s) -> s.startsWith("A"))
.peek(e -> System.out.println("filter res: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("map res: " + e))
.collect(Collectors.toList());
System.out.println(res);
System.out.println("");
System.out.println("");
// limit
// 限制回傳的 elements 數量
System.out.println("limit");
List res2 = names.stream().filter((s) -> s.startsWith("A"))
.map(String::toUpperCase)
.limit(1)
.collect(Collectors.toList());
System.out.println(res2);
System.out.println("");
System.out.println("");
// skip
// 略過前面幾個 elements
System.out.println("skip");
List res3 = names.stream().filter((s) -> s.startsWith("A"))
.map(String::toUpperCase)
.skip(1)
.collect(Collectors.toList());
System.out.println(res3);
System.out.println("");
System.out.println("");
}
Terminal Operations
method | description |
---|---|
forEach | 對 steam 所有 elements 進行運算 |
forEachOrdered | 會依照原本 stream 元素的順序輸出,平行化 stream 後,順序也會固定 |
toArray | 轉換為 Array |
reduce | 把一個Stream的所有元素按照聚合函數聚合成一個結果 |
collect | 透過 Collector 可將 Stream 裡面的 elements 再轉換為 Collection |
min | 找最小元素 |
max | 找最大元素 |
count | 元素數量 |
anyMatch | 檢查是否有任何一個元素,滿足給定的條件 |
allMatch | 檢查是否有所有的元素,都滿足給定的條件 |
nonMatch | 檢查是否沒有任何一個元素,滿足給定的條件 |
findFirst | 回傳 stream 的第一個元素 |
findAny | 回傳 stream 的任一個元素 |
private static void terminalOperation() {
List<String> names = new ArrayList<>();
names.add("Allen");
names.add("George");
names.add("Anderson");
names.add("Danny");
// forEach
System.out.println("forEach");
names.stream().forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("");
// forEachOrdered
System.out.println("1. Sequential Stream");
System.out.println("1.1 forEach 會依照原本的順序輸出");
names.stream().forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("1.2 forEachOrdered 會依照原本的順序輸出");
names.stream().forEachOrdered(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("2. Parallel Stream");
System.out.println("2.1 forEach 輸出順序不固定");
names.stream().parallel().forEach(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("2.2 forEachOrdered 會依照原本的順序輸出");
names.stream().parallel().forEachOrdered(n -> System.out.print(n + " "));
System.out.println("");
System.out.println("");
// toArray
System.out.println("toArray");
String[] nameArray = names.stream().toArray(String[]::new);
for( String n: nameArray) {
System.out.print(n+" ");
}
System.out.println("");
System.out.println("");
// reduce 傳入 BinaryOperator
System.out.println("reduce");
int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).reduce(0, (s, n) -> s + n);
int mul = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).reduce(1, (m, n) -> m * n);
System.out.println("累加 sum="+sum);
System.out.println("連乘 mul="+mul);
System.out.println("");
System.out.println("");
// collect
System.out.println("collect");
List<String> filterNames = names.stream().filter((s) -> s.startsWith("A"))
.collect(Collectors.toList());
for( String n: filterNames) {
System.out.print(n+" ");
}
System.out.println("");
System.out.println("");
// min, max
System.out.println("min, max");
Optional<String> minName = names.stream()
.min(Comparator.comparing(String::valueOf));
if( minName.isPresent() ) {
System.out.println("min = "+minName.get());
}
Optional<String> maxName = names.stream()
.max(Comparator.comparing(String::valueOf));
if( maxName.isPresent() ) {
System.out.println("max = "+maxName.get());
}
System.out.println("");
System.out.println("");
// count
System.out.println("count");
long count = names.stream().filter((s) -> s.startsWith("A"))
.count();
System.out.println("count="+count);
System.out.println("");
System.out.println("");
// anyMatch allMatch noneMatch
System.out.println("anyMatch allMatch noneMatch");
boolean anyMatch = names.stream()
.anyMatch(n -> Character.isUpperCase(n.charAt(0)));
boolean allMatch = names.stream()
.allMatch(n -> Character.isUpperCase(n.charAt(0)));
boolean noneMatch = names.stream()
.noneMatch(n -> Character.isUpperCase(n.charAt(0)));
System.out.println("anyMatch="+anyMatch);
System.out.println("allMatch="+allMatch);
System.out.println("noneMatch="+noneMatch);
System.out.println("");
System.out.println("");
// findFirst findAny
System.out.println("findFirst findAny");
Optional<String> findFirst = names.stream().filter((s) -> s.startsWith("A"))
.findFirst();
if( findFirst.isPresent() ) {
System.out.println("findFirst = "+findFirst.get());
}
Optional<String> findAny = names.stream().filter((s) -> s.startsWith("A"))
.findAny();
if( findAny.isPresent() ) {
System.out.println("findAny = "+findAny.get());
}
System.out.println("");
System.out.println("");
}
parallel
parallelStream() 及 stream().parallel() 可將 stream 平行化
private static void parallel() {
// List.stream()
// List.parallelStream()
System.out.println("List.stream()");
List<Integer> list = new ArrayList<Integer>();
for (int i = 1; i < 10; i++) {
list.add(i);
}
Stream<Integer> stream3 = list.stream();
stream3.forEach(p -> System.out.print(p + " "));
System.out.println("");
System.out.println("List.parallelStream()");
Stream<Integer> stream4 = list.parallelStream();
stream4.forEach(p -> System.out.print(p + " "));
System.out.println("");
// stream().parallel()
System.out.println("stream().parallel()");
Stream<Integer> stream5 = list.stream().parallel();
stream5.forEach(p -> System.out.print(p + " "));
System.out.println("");
}
References
Java Stream API (with Examples) - HowToDoInJava
Understanding Java 8 Streams API - amitph
10 Examples of Stream API in Java 8 - count + filter + map + distinct + collect() Examples | Java67
Difference Between parallelStream() and stream().parallel() in Java | Baeldung