跳到主要内容

Java Stream创建

Stream(流)是Java 8引入的一个强大特性,它让我们能够以声明式的方式处理数据集合。在使用Stream API之前,我们首先需要了解如何创建Stream对象。本文将全面介绍Java中创建Stream的各种方法。

什么是Stream?

Stream是Java中处理集合的函数式编程抽象。它不是数据结构,而是从数据源产生的一种视图,它支持各种聚合操作,如过滤、映射、归约等。

备注

Stream API的主要优势在于它提供了一种高级声明式编程模型,极大地简化了集合的操作。

从集合创建Stream

集合是最常见的数据源,所有的Collection接口的实现类都可以通过stream()方法获取流。

使用Collection.stream()

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

public class CollectionStreamExample {
public static void main(String[] args) {
// 创建一个List集合
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
fruits.add("Watermelon");

// 从List创建Stream
Stream<String> fruitStream = fruits.stream();

// 使用Stream进行操作
fruitStream.filter(fruit -> fruit.length() > 5)
.forEach(System.out::println);
}
}

输出:

Banana
Orange
Watermelon

使用Collection.parallelStream()

如果你需要并行处理集合元素,可以使用parallelStream()方法:

java
Stream<String> parallelFruitStream = fruits.parallelStream();
提示

parallelStream()适用于大数据集和多核处理器环境,能够显著提高处理速度。但并不是所有情况下都比顺序流更高效,尤其是对于小数据集。

从数组创建Stream

Java提供了Arrays.stream()方法来从数组创建流。

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

public class ArrayStreamExample {
public static void main(String[] args) {
// 创建一个字符串数组
String[] fruitsArray = {"Apple", "Banana", "Orange", "Watermelon"};

// 从数组创建Stream
Stream<String> fruitsStream = Arrays.stream(fruitsArray);

// 使用Stream进行操作
fruitsStream.map(String::toUpperCase)
.forEach(System.out::println);
}
}

输出:

APPLE
BANANA
ORANGE
WATERMELON

指定数组的部分范围

你还可以指定数组的开始索引和结束索引(不包含结束索引)来创建Stream:

java
Stream<String> partialStream = Arrays.stream(fruitsArray, 1, 3); // 只获取索引1和2的元素
partialStream.forEach(System.out::println);

输出:

Banana
Orange

从值创建Stream

如果你只有几个固定的值,可以使用Stream.of()方法直接创建流。

java
import java.util.stream.Stream;

public class StreamOfExample {
public static void main(String[] args) {
// 使用Stream.of()创建Stream
Stream<String> fruitsStream = Stream.of("Apple", "Banana", "Orange", "Watermelon");

// 使用Stream进行操作
fruitsStream.sorted()
.forEach(System.out::println);
}
}

输出:

Apple
Banana
Orange
Watermelon

创建空Stream

有时你可能需要创建一个不包含任何元素的空流:

java
Stream<String> emptyStream = Stream.empty();

使用Stream.builder()

Stream.builder()方法提供了一个构建器API,用于逐步构建流:

java
import java.util.stream.Stream;

public class StreamBuilderExample {
public static void main(String[] args) {
// 使用Stream.builder()创建Stream
Stream<String> fruitsStream = Stream.<String>builder()
.add("Apple")
.add("Banana")
.add("Orange")
.add("Watermelon")
.build();

fruitsStream.forEach(System.out::println);
}
}

使用Stream.iterate()创建无限流

Stream.iterate()方法可以创建一个无限流,通过对初始值不断应用指定函数生成后续元素:

java
import java.util.stream.Stream;

public class InfiniteStreamExample {
public static void main(String[] args) {
// 使用Stream.iterate()创建无限流
Stream<Integer> infiniteStream = Stream.iterate(1, n -> n + 2);

// 限制只取前5个元素
infiniteStream.limit(5)
.forEach(System.out::println);
}
}

输出:

1
3
5
7
9

Java 9增强了iterate方法,添加了一个谓词参数来控制流的结束:

java
// Java 9+
Stream<Integer> finiteStream = Stream.iterate(1, n -> n < 10, n -> n + 2);
finiteStream.forEach(System.out::println);

使用Stream.generate()创建无限流

Stream.generate()方法接收一个Supplier函数,不断调用该函数生成元素:

java
import java.util.Random;
import java.util.stream.Stream;

public class GenerateStreamExample {
public static void main(String[] args) {
// 使用Stream.generate()创建无限流,生成随机数
Stream<Integer> randomStream = Stream.generate(() -> new Random().nextInt(100));

// 限制只取前5个元素
randomStream.limit(5)
.forEach(System.out::println);
}
}

输出(结果将随机):

47
83
12
69
35

从文件创建Stream

Java NIO提供了从文件创建流的方法:

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class FileStreamExample {
public static void main(String[] args) {
try {
Path path = Paths.get("example.txt");
// 从文件中的每一行创建Stream
Stream<String> linesStream = Files.lines(path);

linesStream.forEach(System.out::println);
linesStream.close(); // 记得关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}

从基本类型创建Stream

对于基本类型(如int, long, double),Java提供了特殊的流实现以避免装箱/拆箱操作带来的性能开销。

java
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.DoubleStream;

public class PrimitiveStreamExample {
public static void main(String[] args) {
// IntStream
IntStream intStream = IntStream.range(1, 5); // 1, 2, 3, 4
intStream.forEach(System.out::print);
System.out.println();

// 包含结束值
intStream = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5
intStream.forEach(System.out::print);
System.out.println();

// LongStream
LongStream longStream = LongStream.rangeClosed(1, 5);
System.out.println("Sum: " + longStream.sum());

// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.1, 2.2, 3.3, 4.4);
System.out.println("Average: " + doubleStream.average().orElse(0));
}
}

输出:

1234
12345
Sum: 15
Average: 2.75

字符串转Stream

可以使用String的chars()方法将字符串转换为IntStream:

java
import java.util.stream.IntStream;

public class StringStreamExample {
public static void main(String[] args) {
String text = "Hello";
IntStream charStream = text.chars();

// 打印每个字符的ASCII值
charStream.forEach(System.out::println);

// 或者转换回字符
text.chars()
.mapToObj(c -> (char) c)
.forEach(System.out::print);
}
}

实际应用案例

处理电子商务网站的产品数据

假设我们有一个电子商务网站,需要处理大量产品信息:

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

class Product {
private String name;
private String category;
private double price;

public Product(String name, String category, double price) {
this.name = name;
this.category = category;
this.price = price;
}

public String getName() { return name; }
public String getCategory() { return category; }
public double getPrice() { return price; }

@Override
public String toString() {
return name + " (" + category + "): $" + price;
}
}

public class EcommerceExample {
public static void main(String[] args) {
// 创建产品列表
List<Product> products = Arrays.asList(
new Product("iPhone 13", "Electronics", 999.99),
new Product("Samsung Galaxy", "Electronics", 899.99),
new Product("Laptop Dell XPS", "Computers", 1299.99),
new Product("Gaming Mouse", "Accessories", 59.99),
new Product("Keyboard", "Accessories", 49.99)
);

// 创建流并筛选电子产品
List<Product> electronics = products.stream()
.filter(p -> p.getCategory().equals("Electronics"))
.collect(Collectors.toList());

System.out.println("Electronics products:");
electronics.forEach(System.out::println);

// 计算所有产品的平均价格
double avgPrice = products.stream()
.mapToDouble(Product::getPrice)
.average()
.orElse(0.0);

System.out.println("\nAverage product price: $" + avgPrice);

// 按类别分组产品
System.out.println("\nProducts by category:");
products.stream()
.collect(Collectors.groupingBy(Product::getCategory))
.forEach((category, prods) -> {
System.out.println(category + ":");
prods.forEach(p -> System.out.println(" " + p));
});
}
}

输出:

Electronics products:
iPhone 13 (Electronics): $999.99
Samsung Galaxy (Electronics): $899.99

Average product price: $662.19

Products by category:
Electronics:
iPhone 13 (Electronics): $999.99
Samsung Galaxy (Electronics): $899.99
Computers:
Laptop Dell XPS (Computers): $1299.99
Accessories:
Gaming Mouse (Accessories): $59.99
Keyboard (Accessories): $49.99

总结

Java Stream API提供了多种创建流的方式,使我们能够从不同的数据源轻松生成流并进行处理。本文介绍了以下创建Stream的方法:

  1. 从集合创建:collection.stream()collection.parallelStream()
  2. 从数组创建:Arrays.stream(array)
  3. 从值创建:Stream.of(val1, val2, ...)
  4. 创建空流:Stream.empty()
  5. 使用构建器:Stream.builder()
  6. 创建无限流:Stream.iterate()Stream.generate()
  7. 从文件创建:Files.lines()
  8. 从基本类型创建:IntStream, LongStream, DoubleStream
  9. 从字符串创建:string.chars()
警告

使用无限流时,务必使用limit()或者其他终止操作来限制流的大小,否则可能导致程序无法终止。

练习

  1. 创建一个包含1到10所有偶数的Stream,并计算它们的和。
  2. 使用Stream.generate()创建一个包含10个随机整数(1-100之间)的Stream,并找出其中的最大值。
  3. 从一个集合中筛选出所有长度大于5的字符串,并将它们转换为大写形式。
  4. 使用IntStream创建一个1到20的流,并计算所有能被3整除的数字的平均值。

延伸阅读

掌握Stream的创建是使用Java Stream API的第一步,接下来你可以学习如何使用各种流操作(如filter、map、reduce等)来处理数据。