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()
  • 基本型別使用 IntStreamLongStreamDoubleStream 避免裝箱
  • 檔案 Stream 記得使用 try-with-resources