Java Stream 建立
Stream 可以透過多種方式建立,了解這些方法有助於選擇最適合的方式。
從集合建立
// List
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
// Set
Set<String> set = Set.of("a", "b", "c");
Stream<String> stream2 = set.stream();
// 平行 Stream
Stream<String> parallelStream = list.parallelStream();
從陣列建立
// 使用 Arrays.stream()
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
// 部分陣列
Stream<String> stream2 = Arrays.stream(array, 1, 3); // ["b", "c"]
// 使用 Stream.of()
Stream<String> stream3 = Stream.of("a", "b", "c");
Stream<String> stream4 = Stream.of(array);
使用 Stream.of()
// 多個元素
Stream<String> stream = Stream.of("a", "b", "c");
// 單一元素
Stream<String> single = Stream.of("hello");
// 空 Stream
Stream<String> empty = Stream.empty();
使用 Stream.builder()
動態建立 Stream:
Stream.Builder<String> builder = Stream.builder();
builder.add("a");
builder.add("b");
builder.add("c");
Stream<String> stream = builder.build();
// 鏈式呼叫
Stream<String> stream2 = Stream.<String>builder()
.add("a")
.add("b")
.add("c")
.build();
使用 Stream.generate()
無限 Stream,需搭配 limit():
// 固定值
Stream<String> stream = Stream.generate(() -> "Hello")
.limit(5); // ["Hello", "Hello", "Hello", "Hello", "Hello"]
// 隨機值
Stream<Double> randoms = Stream.generate(Math::random)
.limit(10);
// 遞增 ID
AtomicInteger counter = new AtomicInteger(0);
Stream<Integer> ids = Stream.generate(counter::getAndIncrement)
.limit(100);
使用 Stream.iterate()
迭代產生元素:
// 傳統迭代(無限)
Stream<Integer> stream = Stream.iterate(0, n -> n + 2)
.limit(5); // [0, 2, 4, 6, 8]
// 有界迭代(Java 9+)
Stream<Integer> stream2 = Stream.iterate(0, n -> n < 10, n -> n + 2);
// [0, 2, 4, 6, 8]
// 費氏數列
Stream.iterate(new int[]{0, 1}, arr -> new int[]{arr[1], arr[0] + arr[1]})
.limit(10)
.map(arr -> arr[0])
.forEach(System.out::println);
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
基本型別 Stream
避免裝箱開銷:
// IntStream
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
IntStream range = IntStream.range(1, 5); // [1, 2, 3, 4)
IntStream rangeClosed = IntStream.rangeClosed(1, 5); // [1, 2, 3, 4, 5]
// LongStream
LongStream longStream = LongStream.of(1L, 2L, 3L);
LongStream range = LongStream.range(1, 1000000);
// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.1, 2.2, 3.3);
// 從陣列
int[] array = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(array);
轉換
// 裝箱
IntStream intStream = IntStream.of(1, 2, 3);
Stream<Integer> boxed = intStream.boxed();
// 拆箱
Stream<Integer> stream = Stream.of(1, 2, 3);
IntStream intStream = stream.mapToInt(Integer::intValue);
// 型別轉換
IntStream.range(1, 5)
.mapToDouble(i -> i * 1.5)
.forEach(System.out::println);
從其他來源建立
字串
// 字元 Stream
"Hello".chars().forEach(c -> System.out.print((char) c));
// 按行分割
"a\nb\nc".lines().forEach(System.out::println);
檔案
// 讀取檔案行
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
lines.forEach(System.out::println);
}
// 列出目錄
try (Stream<Path> paths = Files.list(Paths.get("."))) {
paths.forEach(System.out::println);
}
// 遞迴遍歷
try (Stream<Path> paths = Files.walk(Paths.get("."))) {
paths.filter(Files::isRegularFile)
.forEach(System.out::println);
}
正規表達式
Pattern pattern = Pattern.compile(",");
Stream<String> stream = pattern.splitAsStream("a,b,c,d"); // ["a", "b", "c", "d"]
Random
Random random = new Random();
// 隨機整數
IntStream ints = random.ints(10); // 10 個隨機整數
IntStream ints2 = random.ints(10, 0, 100); // 10 個 0-99 的整數
// 隨機浮點數
DoubleStream doubles = random.doubles(10);
DoubleStream doubles2 = random.doubles(10, 0.0, 1.0);
BufferedReader
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
reader.lines().forEach(System.out::println);
}
合併 Stream
Stream<String> stream1 = Stream.of("a", "b");
Stream<String> stream2 = Stream.of("c", "d");
// 合併
Stream<String> combined = Stream.concat(stream1, stream2);
// ["a", "b", "c", "d"]
// 多個 Stream
Stream<String> combined2 = Stream.of(stream1, stream2, Stream.of("e"))
.flatMap(s -> s);
實際範例
產生測試資料
List<User> users = IntStream.range(1, 101)
.mapToObj(i -> new User("User" + i, 20 + i % 30))
.collect(Collectors.toList());
日期範圍
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
Stream<LocalDate> dates = start.datesUntil(end.plusDays(1));
// 或
Stream<LocalDate> dates2 = Stream.iterate(start, d -> d.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1);
字母表
// a-z
IntStream.rangeClosed('a', 'z')
.mapToObj(c -> String.valueOf((char) c))
.forEach(System.out::print); // abcdefghijklmnopqrstuvwxyz
建立方式比較
| 方式 | 用途 | 特點 |
|---|---|---|
collection.stream() | 集合轉 Stream | 最常用 |
Arrays.stream() | 陣列轉 Stream | 支援部分範圍 |
Stream.of() | 多個元素 | 簡潔 |
Stream.generate() | 無限 Stream | 需要 limit |
Stream.iterate() | 迭代產生 | 有規律的序列 |
IntStream.range() | 數值範圍 | 效能好 |
Stream.builder() | 動態建立 | 彈性大 |
重點整理
- 集合使用
.stream()或.parallelStream() - 陣列使用
Arrays.stream()或Stream.of() - 無限 Stream 使用
generate()或iterate(),記得加limit() - 數值範圍使用
IntStream.range()或rangeClosed() - 基本型別使用
IntStream、LongStream、DoubleStream避免裝箱 - 檔案 Stream 記得使用 try-with-resources