Java Streams:初學者的指南

你好,未來的Java大師!今天,我們將要踏上一段令人興奮的旅程,進入Java Streams的世界。如果你是編程新手,別擔心——我將成為你的友好指南,我們會一步步來。在本教程結束時,你將能像專家一樣流式處理數據!

Java - Streams

Java中的Stream是什麼?

想像你在一個傳送帶壽司餐廳。壽司盤不斷地在你面前移動,你可以挑選你想要的。這就是Java中Stream的本質——它是一個元素的序列,你可以一個接一個地處理,而不必將它們全部存儲在記憶體中。

Streams在Java 8中引入,以使處理數據集合更為輕鬆和高效。它們讓我們能以更具說明性的方式對數據進行操作,告訴Java我們想做什麼,而不是如何做。

在Java中生成Streams

讓我們從創建我們的第一個Stream開始。有幾種方法可以做到這點,但我們從一個簡單的例子開始:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {
public static void main(String[] args) {
// 從List創建Stream
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
Stream<String> fruitStream = fruits.stream();

// 直接創建Stream
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
}
}

在這個例子中,我們創建了兩個Streams。第一個,fruitStream,是從水果列表創建的。第二個,numberStream,是使用Stream.of()方法直接創建的。

常見的Stream操作

現在有了我們的Streams,讓我們看看一些我們可以對它們進行的常見操作。

forEach方法

forEach方法就像一個友好的機器人,它會遍歷Stream中的每個元素並對其進行操作。讓我們用它來打印我們的水果:

fruits.stream().forEach(fruit -> System.out.println(fruit));

這將打印:

apple
banana
cherry
date

在這裡,fruit -> System.out.println(fruit)被稱為lambda表達式。它是一種告訴Java對每個元素做什麼的簡潔方式。

map方法

map方法就像一根魔杖,它可以轉換Stream中的每個元素。讓我們用它將我們的所有水果轉換為大寫:

List<String> upperCaseFruits = fruits.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseFruits);

這將打印:

[APPLE, BANANA, CHERRY, DATE]

String::toUpperCase是一個方法引用,這是Java的另一個便捷特性,就像說“對每個String使用toUpperCase方法”。

filter方法

filter方法就像俱樂部的保鏢,只讓某些元素通過。讓我們用它來保留以'a'開頭的水果:

List<String> aFruits = fruits.stream()
.filter(fruit -> fruit.startsWith("a"))
.collect(Collectors.toList());
System.out.println(aFruits);

這將打印:

[apple]

limit方法

limit方法就像說“我只想要這麼多,謝謝!”它限制了Stream中的元素數量:

List<String> firstTwoFruits = fruits.stream()
.limit(2)
.collect(Collectors.toList());
System.out.println(firstTwoFruits);

這將打印:

[apple, banana]

sorted方法

sorted方法就像整理你的書架。它將元素按照順序排列:

List<String> sortedFruits = fruits.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedFruits);

這將打印:

[apple, banana, cherry, date]

並行處理

關於Streams的一個很酷的事情是,它們可以很容易地進行並行處理,從而有潛在地在大型數據集上加速操作。你可以這樣創建一個並行Stream:

fruits.parallelStream().forEach(fruit -> System.out.println(fruit + " " + Thread.currentThread().getName()));

這可能會打印像這樣的東西:

banana ForkJoinPool.commonPool-worker-1
apple main
date ForkJoinPool.commonPool-worker-2
cherry ForkJoinPool.commonPool-worker-3

每次你運行它時,順序可能會不同,因為水果正在被並行處理!

Collectors

Collectors是特殊工具,它們幫助我們收集Stream操作的結果。我們在例子中一直在使用collect(Collectors.toList())將我們的Stream結果轉換回Lists。還有許多其他有用的Collectors:

// 將元素連接成字符串
String fruitString = fruits.stream().collect(Collectors.joining(", "));
System.out.println(fruitString);  // 打印: apple, banana, cherry, date

// 計數元素
long fruitCount = fruits.stream().collect(Collectors.counting());
System.out.println(fruitCount);  // 打印: 4

// 組織元素
Map<Character, List<String>> fruitGroups = fruits.stream()
.collect(Collectors.groupingBy(fruit -> fruit.charAt(0)));
System.out.println(fruitGroups);  // 打印: {a=[apple], b=[banana], c=[cherry], d=[date]}

統計

Streams還可以幫助我們對數字數據計算統計:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());

這將打印:

Count: 5
Sum: 15
Min: 1
Max: 5
Average: 3.0

Java Streams範例

讓我們將所有這些東西結合起來,創造一個更複雜的例子。想像我們有一個學生列表,他們的年齡和成績如下:

class Student {
String name;
int age;
double grade;

Student(String name, int age, double grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}

public class StreamExample {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 22, 90.5),
new Student("Bob", 20, 85.0),
new Student("Charlie", 21, 92.3),
new Student("David", 23, 88.7)
);

// 獲得21歲以上學生的平均成績
double averageGrade = students.stream()
.filter(student -> student.age > 21)
.mapToDouble(student -> student.grade)
.average()
.orElse(0.0);

System.out.println("21歲以上學生的平均成績: " + averageGrade);

// 獲得成績最高的兩名學生的名字
List<String> topStudents = students.stream()
.sorted((s1, s2) -> Double.compare(s2.grade, s1.grade))
.limit(2)
.map(student -> student.name)
.collect(Collectors.toList());

System.out.println("成績最高的兩名學生: " + topStudents);
}
}

這個例子展示了我們如何將多個Stream操作串聯起來,以簡潔且易於閱讀的方式執行複雜的數據處理任務。

那就是它,夥伴們!你剛剛踏入了Java Streams的世界。記住,熟能生巧,所以不要害怕嘗試這些概念。在你意識到之前,你將會像專家一樣流式處理數據!快樂編程!

Credits: Image by storyset