Stream API 是 Java 中引入的一種新的資料處理方法。它提供了一種高效且易於使用的方法來處理資料集合。Stream API 支援函數語言程式設計,可以讓我們以簡潔、優雅的方式進行資料操作,還有使用 Stream 的兩大原因:
先展示一段簡單的流式程式設計:
import java.util.Random;
public class Randoms {
public static void main(String[] args) {
// 隨機展示 5 至 20 之間不重複的整數並進行排序
new Random(47)
.ints(5, 20)
.distinct() // 使流中的整數不重複
.limit(7) // 獲取前 7 個元素
.sorted() // 排序
.forEach(System.out::println);
}
}
輸出結果:
6
10
13
16
17
18
19
實際上函數式的程式設計風格是宣告式(Declarative programming)的,它宣告了要做什麼, 而不是指明(每一步)如何做。
相同的程式,相比宣告式風格,命令式(Imperative)程式設計的形式(指明每一步如何做),程式碼閱讀起來會更難理解:
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
public class ImperativeRandoms {
public static void main(String[] args) {
Random rand = new Random(47);
SortedSet<Integer> rints = new TreeSet<>();
while (rints.size() < 7) {
int r = rand.nextInt(20);
if (r < 5) continue;
rints.add(r);
}
System.out.println(rints);
}
}
輸出結果:
[7, 8, 9, 11, 13, 15, 18]
所以使用流式程式設計的幾個理由:
Java 8 通過在新增介面中新增 default 關鍵字,通過預設方法的方式將流式 Stream 方法平滑地嵌入到現有的類中,
流操作的型別有三種:
通過 Stream.of()
很容見的將一組元素轉化為流:
import java.util.stream.Stream;
public class StreamOf {
public static void main(String[] args) {
// 建立流
Stream.of(new Bubble(1), new Bubble(2), new Bubble(3))
.forEach(System.out::println);
Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!")
.forEach(System.out::print);
System.out.println();
Stream.of(3.14159, 2.718, 1.618)
.forEach(System.out::println);
}
}
輸出結果:
Bubble 1
Bubble 2
Bubble 3
It's a wonderful day for pie!
3.14159
2.718
1.618
通過 stream()
方法很容易將傳統的集合轉化為 Stream:
public class CollectionToStream {
public static void main(String[] args) {
List<Bubble> bubbles = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
System.out.println(bubbles.stream() // 將集合轉換成為流
.mapToInt(b -> b.i) // 獲取流中所有元素,對元素進行應用操作,併產生新的物件,這裡的 mapToInt 中間操作會轉換成為包含整型數位的 IntStream
.sum()); // 合計
HashSet<String> w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" ")));
w.stream()
.map(x -> x + " ")
.forEach(System.out::print); // stream 遍歷並且列印 Set 中的元素
System.out.println();
Map<String, Double> m = new HashMap<>();
m.put("pi", 3.14159);
m.put("e", 2.718);
m.put("phi", 1.618);
m.entrySet().stream()
.map(e -> e.getKey() + ": " + e.getValue())
.forEach(System.out::println); // stream 遍歷並且列印 Map 中的元素
}
}
輸出結果:
6
a pie! It's for wonderful day
phi: 1.618
e: 2.718
pi: 3.14159
Java 8 的 Random
類也整合流的方法,很方便的建立亂數流:
import java.util.Random;
import java.util.stream.Stream;
// 生成亂數流
public class RandomGenerators {
public static <T> void show(Stream<T> stream) {
stream.limit(4).forEach(System.out::println);
System.out.println("++++++++++");
}
public static void main(String[] args) {
Random rand = new Random(47);
show(rand.ints().boxed());
show(rand.longs().boxed());
show(rand.doubles().boxed());
// 控制上限和下限
show(rand.ints(10, 20).boxed());
show(rand.longs(50, 100).boxed());
show(rand.doubles(20, 30).boxed());
// 控制流大小
show(rand.ints(2).boxed());
show(rand.longs(2).boxed());
show(rand.doubles(2).boxed());
// 控制流大小和上限和下限
show(rand.ints(3, 3, 9).boxed());
show(rand.longs(3, 12, 22).boxed());
show(rand.doubles(3, 11.5, 12.3).boxed());
}
}
輸出結果:
-1172028779
1717241110
-2014573909
229403722
++++++++++
2955289354441303771
3476817843704654257
-8917117694134521474
4941259272818818752
++++++++++
# ……………………
Stream API 對基本資料型別生成流提供便捷的方法,例如對一段整型序列求和,展示新舊程式碼對比
import java.util.stream.IntStream;
public class Ranges {
public static void main(String[] args) {
// 傳統方法
int result = 0;
for (int i = 0; i < 20; i++) {
result += i;
}
System.out.println(result);
// 使用流
System.out.println(IntStream.range(0, 20).sum());
}
}
輸出結果:
190
190
Stream API 還可以結合 Supplier
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamGenerateExample {
public static void main(String[] args) {
Random random = new Random();
Stream<Integer> randomNumbers = Stream.generate(random::nextInt);
// 生成 10 個亂數放入集合中
List<Integer> integers = randomNumbers
.limit(10)
.collect(Collectors.toList());
integers.forEach(System.out::println);
}
}
輸出結果:
514000574
1771591868
600289224
-1474939200
-276604430
-876159270
509964750
-497958443
811408347
703285366
Stream.iterate
是 Java 8 引入的 Stream API 的一部分,它接受一個種子值(seed)和一個一元函數(unary operator),然後生成一個無限的、順序的流。流中的每個元素都是通過對前一個元素應用一元函數生成的。與 Stream.generate
類似, Stream.iterate
常常用於生成一個斐波那契數列,程式碼範例:
import java.util.stream.Stream;
public class Fibonacci {
int x = 1;
Stream<Integer> numbers() {
return Stream.iterate(0, i -> {
int result = x + i;
x = i;
return result;
});
}
public static void main(String[] args) {
Fibonacci fbi = new Fibonacci();
fbi.numbers()
.skip(20) // 丟棄前 20 個
.limit(10) // 取 10 個
.forEach(System.out::println);
}
}
輸出結果:
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
Stream.iterate
和 Stream.generate
都是 Java 8 引入的 Stream API 的一部分,它們用於生成無限的順序流。稍不留神就容易把它們搞混,可以通過以下的方式來區分它們:
iterate:
generate:
Supplier<T>
型別的引數。Supplier
生成的。Arrays
類中的 stream()
方法用於將陣列轉換為 Stream
。以下是使用 Arrays.stream()
方法的一些範例:
import java.util.Arrays;
public class ArraysStreamExample {
public static void main(String[] args) {
// IntStream
int[] integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Arrays.stream(integer)
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
// Stream<String>
String[] words = {"hello", "world", "java", "stream"};
Arrays.stream(words)
.map(String::toUpperCase)
.forEach(System.out::println);
// DoubleStream
double[] doubles = {1.0, 3.5, 7.2, 8.8, 12.0, 15.5};
double average = Arrays.stream(doubles)
.average()
.orElse(0.0);
System.out.println("Average: " + average);
}
}
輸出結果:
2
4
6
8
10
HELLO
WORLD
JAVA
STREAM
Average: 8.0
中間操作(intermediate operations)是那些在 Stream 上執行的操作,但不會觸發流的處理。它們通常返回一個新的 Stream,該 Stream 包含應用了某種操作後的元素。
以下是一些常見的中間操作:
filter(Predicate<T> predicate)
:根據給定的謂詞篩選 Stream 中的元素。map(Function<T, R> mapper)
:將 Stream 中的每個元素轉換為另一種型別,根據給定的對映函數。flatMap(Function<T, Stream<R>> mapper)
:將每個元素轉換為另一個 Stream,然後將所有這些流連線成一個 Stream。distinct()
:返回一個去重後的 Stream,其中每個元素只出現一次。sorted()
:返回一個按自然順序排序的 Stream。sorted(Comparator<T> comparator)
:根據給定的比較器返回一個排序後的 Stream。peek(Consumer<T> action)
:對 Stream 中的每個元素執行給定的操作,但不會改變 Stream 中的元素。通常用於偵錯目的。注意:中間操作是惰性的,也就是說,它們只在終端操作被呼叫時才會實際執行。例如 forEach
、collect
、reduce
等
Stream.peek()
是一箇中間操作,它接受一個 Consumer
,並允許您在流的每個元素上執行某個操作,同時保持流的元素不變。通常用於偵錯目的,因為它允許您檢視流處理過程中的中間結果。範例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Peeking {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Alice", "Bob", "Cindy", "David");
List<String> result = names.stream()
.filter(name -> name.length() > 3)
.peek(name -> System.out.println("Filtered name: " + name))
.map(String::toUpperCase)
.peek(name -> System.out.println("Mapped name: " + name))
.collect(Collectors.toList());
System.out.println("Result: " + result);
}
}
以上程式碼邏輯是:
filter
操作篩選出長度大於3的名字peek()
來列印篩選後的名字map
操作將篩選後的名字轉換為大寫形式peek()
來列印轉換後的名字collect
操作將流中的元素收集到一個新的 List
中,並列印結果輸出結果:
Filtered name: John
Mapped name: JOHN
Filtered name: Alice
Mapped name: ALICE
Filtered name: Cindy
Mapped name: CINDY
Filtered name: David
Mapped name: DAVID
Result: [JOHN, ALICE, CINDY, DAVID]
Stream.sorted()
可以通過內建的比較器,很容易的對集合進行排序
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class Peeking {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Alice", "Bob", "Cindy", "David");
List<String> result = names.stream()
.sorted(Comparator.reverseOrder())
.map(String::toLowerCase)
.collect(Collectors.toList());
System.out.println("Result: " + result);
}
}
輸出結果:
Result: [john, david, cindy, bob, alice]
常見的場景,Stream API 提供以下函數進行過濾:
簡單看一個範例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class DistinctAndFilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 1, 4, 5, 4, 6, 7, 7, 8);
List<Integer> result = numbers.stream()
.distinct() // 1: 消除重複元素
.filter(e -> e % 2 == 0) //2: 篩選出偶數
.collect(Collectors.toList()); //3: 將結果放入 List 中
System.out.println(result);
}
}
以上程式碼邏輯很簡單:
distinct()
方法刪除重複元素filter()
方法篩選出偶數collect()
方法將處理後的 Stream 轉換回 List輸出結果:
[2, 4, 6, 8]
對元素的操作主要通過 map(Function)
來完成,在上面的範例程式碼中也有看到過,常見於以下場景:
類似函數 mapToInt,mapToLong,mapToDouble 操作都一樣,只是結果分別為:IntStream,LongStream,DoubleStream
以下是一個簡單的使用範例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Using map(Function) to square each number
List<Integer> squaredNumbers = numbers.stream()
.map(number -> number * number)
.collect(Collectors.toList());
System.out.println("Original list: " + numbers);
System.out.println("Squared numbers: " + squaredNumbers);
}
}
輸出結果:
Original list: [1, 2, 3, 4, 5]
Squared numbers: [1, 4, 9, 16, 25]
某些情況,我們輸入源的流可能是一個複雜的多層巢狀的資料結構,我們想在處理流資料的同時,順便也更更改它的結構,例如把它展開為一個展平為單層資料結構,那麼 flatMap()
中間函數就會派上用場:
flatMap()
:與 map() 所作的事情相同,但它將這些生成的 Stream 合併為一個單一的 StreamflatMap()
在函數語言程式設計和流式處理中非常有用,因為它可以解決一些常見的資料處理問題。
flatMap()
可以將這些巢狀的資料結構展平為一個單一的流,從而簡化後續的資料處理和操作。flatMap()
可以幫助我們將這些流合併為一個流,從而提高程式碼的可讀性和可維護性。flatMap()
使我們能夠根據流中的每個元素動態生成新的流,並將這些新生成的流合併為一個流。這對於根據流中的資料動態建立資料處理管道非常有用。flatMap()
可以減少對流中資料的遍歷次數,從而提高操作鏈的效率。例如,如果我們需要先對流中的每個元素執行對映操作,然後再執行篩選操作,我們可以使用 flatMap()
將這兩個操作組合在一起,從而減少對流的遍歷次數。展平巢狀資料結構:例如,將一個列表的列表轉換為一個包含所有元素的平面列表:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
List<Integer> flatList = nestedList.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
System.out.println("Nested list: " + nestedList);
System.out.println("Flat list: " + flatList);
}
}
輸出結果:
Nested list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Flat list: [1, 2, 3, 4, 5, 6, 7, 8, 9]
合併多個流:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FlatMapExample {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("d", "e", "f");
List<String> list3 = Arrays.asList("g", "h", "i");
// 建立包含多個列表的流
Stream<List<String>> listsStream = Stream.of(list1, list2, list3);
// 使用 flatMap() 合併多個流
List<String> mergedList = listsStream.flatMap(list -> list.stream())
.collect(Collectors.toList());
System.out.println("Merged list: " + mergedList);
}
}
說明以下上面的範例的程式碼:
listsStream
)flatMap()
方法將這些列表轉換為單獨的流,並將這些流合併為一個流collect()
方法將合併後的流轉換為一個列表(mergedList
)通過使用 flatMap()
,我們可以輕鬆地將多個流合併為一個流,從而簡化資料處理和操作,輸出結果:
Merged list: [a, b, c, d, e, f, g, h, i]
map()
主要用於轉換流中的元素,但保持流的結構不變。flatMap()
和 flatMap(Function)
主要用於將巢狀或多層資料結構展平為單層資料結構。map()
是一個很好的選擇flatMap()
是一個更合適的選擇Optional 主要用於在流中處理一些空元素,但是它還可以應用在程式碼的其他地方,它帶來以下一些好處,例如:
在使用 Stream 時,很多操作都會返回 Optional
物件,例如:
範例程式碼:
import java.util.*;
public class OptionalExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Optional<Integer> firstNumber = numbers.stream().filter(n -> n % 2 == 0).findFirst();
System.out.println("findFirst() example: " + firstNumber.orElse(-1)); // 輸出:2
Optional<Integer> anyNumber = numbers.stream().filter(n -> n > 2).findAny();
System.out.println("findAny() example: " + anyNumber.orElse(-1)); // 輸出:3
Optional<Integer> maxNumber = numbers.stream().max(Comparator.naturalOrder());
System.out.println("max() example: " + maxNumber.orElse(-1)); // 輸出:9
Optional<Integer> minNumber = numbers.stream().min(Comparator.naturalOrder());
System.out.println("min() example: " + minNumber.orElse(-1)); // 輸出:1
OptionalDouble average = numbers.stream().mapToInt(Integer::intValue).average();
System.out.println("average() example: " + (average.isPresent() ? average.getAsDouble() : -1)); // 輸出:5.0
}
}
輸出結果:
findFirst() example: 2
findAny() example: 3
max() example: 9
min() example: 1
average() example: 5.0
範例程式碼還展示一些解包 Optional 的操作:
Optional 還提供更靈活的 Supplier 函數式介面的呼叫:
可以通過以下範例程式碼來理解:
public class OptionalExample {
public static void main(String[] args) {
// 生成一個空 Optional
Optional<String> optionalValue = Optional.empty();
// orElseGet() 範例
String value1 = optionalValue.orElseGet(() -> "Default value");
System.out.println("Value 1: " + value1); // 輸出:Value 1: Default value
// orElseThrow() 範例
try {
String value2 = optionalValue.orElseThrow(
() -> new IllegalStateException("Value is not present")
);
System.out.println("Value 2: " + value2);
} catch (RuntimeException e) {
// 輸出:Exception caught: Value is not present
System.out.println("Exception caught: " + e.getMessage());
}
}
}
輸出結果:
Value 1: Default value
Exception caught: Value is not present
我們也可以在自己的程式碼裡面建立 Optional 物件,有以下幾個靜態方法可以使用:
Optional
物件。這個物件不包含任何值Optional
物件。如果傳入的值為 null,將丟擲一個空指標異常Optional
物件範例程式碼:
import java.util.*;
public class OptionalExample {
public static void main(String[] args) {
// 生成一個空的 Optional
Optional<String> emptyOptional = Optional.empty();
// 生成一個不為空的 Optional
Optional<String> optionalWithValue = Optional.of("hello world");
// 可能為空的 Optional
Optional<String> optionalWithValue1 = Optional.ofNullable("hello world"); // 非空值
Optional<String> optionalWithValue2 = Optional.ofNullable(null); // 空值
}
}
建立 Optional 物件後,可以通過內建的函數對 Optional 進行更多的操作,常見的有:
範例程式碼:
import java.util.*;
public class OptionalExample {
public static void main(String[] args) {
Optional<Integer> optionalValue1 = Optional.of(10);
Optional<Integer> optionalValue2 = Optional.empty();
// 使用 filter() 方法
Optional<Integer> filteredValue1 = optionalValue1.filter(value -> value > 5);
System.out.println("Filtered value 1: " + filteredValue1.orElse(-1));
// 使用 map() 方法
Optional<String> mappedValue1 = optionalValue1.map(value -> "Value is: " + value);
System.out.println("Mapped value 1: " + mappedValue1.orElse("Not present"));
// 使用 flatMap() 方法
Optional<String> flatMappedValue1 = optionalValue2.flatMap(value -> Optional.of("Value is: " + value));
System.out.println("FlatMapped value 1: " + flatMappedValue1.orElse("Not present"));
}
}
輸出結果:
Filtered value 1: 10
Mapped value 1: Value is: 10
FlatMapped value 1: Not present
注意:這裡的 flatMap()
與 map()
方法不同,flatMap()
會改變 Optional 結構本身,map()
則不會
終端操作(Terminal Operations)是我們在流管道中所做的最後一件事,通過該操作獲得流中的結果
通過以下方法,可以輕易的收集一個流,並且將流轉為陣列:
範例程式碼:
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamToArrayExample {
public static void main(String[] args) {
// 轉換為物件陣列
Stream<String> stringStream1 = Stream.of("apple", "banana", "cherry");
System.out.println("Stream to array 1: " + Arrays.toString(stringStream1.toArray()));
// 轉換為特定型別的陣列
Stream<String> stringStream2 = Stream.of("apple", "banana", "cherry");
String[] stringArray = stringStream2.toArray(String[]::new);
System.out.println("Stream to array 2: " + Arrays.toString(stringArray));
// 對於基本型別的陣列,可以使用特定的流類
int[] array = IntStream.range(1, 6).toArray();
System.out.println("Stream to array 3: " + Arrays.toString(array));
}
}
輸出結果:
Stream to array 1: [apple, banana, cherry]
Stream to array 2: [apple, banana, cherry]
Stream to array 3: [1, 2, 3, 4, 5]
Stream 中提供 2 個迴圈遍歷方法,用於消費流,分別如下:
我們通過以下範例程式碼來證明:
import java.util.Arrays;
import java.util.List;
public class ForEachOrderedParallelExample {
public static void main(String[] args) {
List<String> stringList = Arrays.asList("apple", "banana", "cherry", "date", "fig", "grape");
// 順序流
System.out.println("Sequential stream:");
stringList.stream().forEachOrdered(System.out::println);
// 並行流 (亂序)
System.out.println("\nParallel stream with forEach:");
stringList.parallelStream().forEach(System.out::println);
// 並行流 (順序)
System.out.println("\nParallel stream with forEachOrdered:");
stringList.parallelStream().forEachOrdered(System.out::println);
}
}
輸出結果:
Sequential stream:
apple
banana
cherry
date
fig
grape
Parallel stream with forEach:
date
grape
fig
cherry
banana
apple
Parallel stream with forEachOrdered:
apple
banana
cherry
date
fig
grape
主要用於將流中的元素收集到不同型別的結果容器,如集合、字串或其他資料結構。它的主要方法有:
我們看看如何把 Stream 收集為常見的 List
,Set
,Map
,還有 String
,範例程式碼:
public class CollectExample {
public static void main(String[] args) {
// 收集到 List
List<String> collectedList = Stream.of("apple", "banana", "orange", "grape")
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Collected List: " + collectedList);
// 收集到 Set
Set<String> collectedSet = Stream.of("apple", "banana", "orange", "grape")
.map(String::toLowerCase)
.collect(Collectors.toSet());
System.out.println("Collected Set: " + collectedSet);
// 收集到 Map
Map<String, Integer> map = Stream.of("apple", "banana", "orange", "grape")
.map(String::toUpperCase)
.collect(Collectors.toMap(s -> s, String::length));
System.out.println("Collected Map: " + map);
// 收集到 String,使用逗號分隔
String joinedString = Stream.of("apple", "banana", "orange", "grape")
.map(String::toUpperCase)
.collect(Collectors.joining(","));
System.out.println("Joined String: " + joinedString);
}
}
輸出結果:
Collected List: [APPLE, BANANA, ORANGE, GRAPE]
Collected Set: [banana, orange, apple, grape]
Collected Map: {APPLE=5, GRAPE=5, BANANA=6, ORANGE=6}
Joined String: APPLE,BANANA,ORANGE,GRAPE
說明:在這裡我們只是簡單介紹了幾個 Collectors 的運用範例。
實際上,它還有一些非常複雜的操作實現,可通過檢視 java.util.stream.Collectors
的 API 檔案瞭解
用於對流中的元素執行累積操作,將它們減少為一個值。reduce
的使用場景包括對流中的元素執行聚合操作,例如求和、求積、求最大值、求最小值等。Stream
裡面的 reduce
方法有以下幾種形式:
Optional<T>
先看看 reduce 的範例程式碼:
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class ReduceExample {
public static void main(String[] args) {
// 求和
OptionalInt sum = IntStream.range(0, 100).reduce(Integer::sum);
sum.ifPresent(System.out::println);
// 求積
OptionalInt numbers = IntStream.range(0, 100).reduce((a, b) -> a * b);
numbers.ifPresent(System.out::println);
// 求最大值
OptionalInt max = IntStream.range(0, 100).reduce(Integer::max);
max.ifPresent(System.out::println);
// 求最小值
OptionalInt min = IntStream.range(0, 100).reduce(Integer::min);
min.ifPresent(System.out::println);
// 使用給定的初始值(identity)和累積器函數,對流中的元素進行累積操作,這裡返回 int 值
int reduced = IntStream.range(0, 100).reduce(10, Integer::sum);
System.out.println(reduced);
}
}
輸出結果:
4950
0
99
0
4960
還有一種 reduce(identity, BiFunction, BinaryOperator)
:更復雜的使用形式暫不介紹。我建議可以顯式地組合 map() 和 reduce() 來更簡單的表達它。
在 Stream 中的終端操作中,提供 allMatch
, anyMatch
, 和 noneMatch
它們用於檢查流中的元素是否滿足某個條件:
PS:以上計算都是短路操作,在匹配第一個結果時,則停止執行計算
下面是它們的使用場景和範例:
import java.util.stream.Stream;
public class MatchExample {
public static void main(String[] args) {
// allMatch:檢查流中的所有元素是否都滿足某個條件
boolean allEven = Stream.of(1, 2, 3, 4, 5).allMatch(num -> num % 2 == 0);
System.out.println(allEven); // 輸出:false
// anyMatch:檢查流中是否存在滿足某個條件的元素
boolean anyEven = Stream.of(1, 2, 3, 4, 5).anyMatch(num -> num % 2 == 0);
System.out.println(anyEven); // 輸出:true
// noneMatch:檢查流中是否不存在滿足某個條件的元素
boolean noneMatch = Stream.of(1, 2, 3, 4, 5).noneMatch(num -> num > 10);
System.out.println(noneMatch); // 輸出:true
}
}
輸出結果:
false
true
true
在 Stream 中的終端操作中,可以根據 Predicate
獲取指定的元素(在 Optional 章節介紹過),查詢函數如下:
程式碼範例:
import java.util.Optional;
import java.util.stream.Stream;
public class FindExample {
public static void main(String[] args) {
// findFirst 範例
Optional<Integer> first = Stream.of(1, 2, 3, 4, 5).filter(num -> num % 2 == 0).findFirst();
first.ifPresent(System.out::println); // 輸出:2
// findAny 範例
Optional<Integer> any = Stream.of(1, 2, 3, 4, 5, 6).filter(num -> num % 2 == 0).findAny();
any.ifPresent(System.out::println); // 輸出:2 或者 4 或者 6
}
}
最後就是一些常見對流進行統計的函數了:
範例程式碼:
// count: 流中的元素個數
System.out.println(Stream.of(1, 2, 3, 4, 5).count()); // 輸出:5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> max = numbers.stream().max(Comparator.naturalOrder());
Optional<Integer> min = numbers.stream().min(Comparator.naturalOrder());
// max, min: 根據給定的比較器查詢流中的最大值或最小值
max.ifPresent(System.out::println); // 輸出:5
min.ifPresent(System.out::println); // 輸出:1
輸出結果:
5
5
1
以下方式是適用於基本資料型別的特殊流,它們提供了對流中數位的基本統計資訊:
程式碼範例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// average: 求取流元素平均值
numbers.stream().mapToInt(Integer::intValue).average().ifPresent(System.out::println); // 輸出:3.0
// max: 數值流求最大值
numbers.stream().mapToInt(Integer::intValue).max().orElse(0); // 輸出:5
// min: 數值流最小值
numbers.stream().mapToInt(Integer::intValue).min().orElse(0); // 輸出:1
// sum: 數值流求和
numbers.stream().mapToInt(Integer::intValue).sum(); // 輸出:15
上例操作對於 LongStream 和 DoubleStream 同樣適用
函數語言程式設計 對 Java 語言的程式設計正規化產生了深遠的影響。在 Stream API 出現之前,處理集合資料通常需要使用 for 迴圈、條件判斷和輔助變數等。這樣的程式碼往往冗長、複雜,不易閱讀和維護。Stream API 的引入,讓我們能以函數語言程式設計的方式處理資料,提高了程式碼的簡潔性、可讀性和可維護性。隨著函數語言程式設計在軟體開發領域的普及,Java 可能會引入更多的函數語言程式設計特性,讓我們能夠更方便地使用函數語言程式設計正規化編寫程式碼。隨著函數語言程式設計在軟體開發領域的普及,Java 可能會引入更多的函數語言程式設計特性,讓我們能夠更方便地使用函數語言程式設計正規化編寫程式碼。 總之,Java 其未來依然充滿潛力。隨著技術的發展和需求的變化,Java 將不斷演進,為開發者提供更好的程式設計體驗