Java Stream创建
Stream(流)是Java 8引入的一个强大特性,它让我们能够以声明式的方式处理数据集合。在使用Stream API之前,我们首先需要了解如何创建Stream对象。本文将全面介绍Java中创建Stream的各种方法。
什么是Stream?
Stream是Java中处理集合的函数式编程抽象。它不是数据结构,而是从数据源产生的一种视图,它支持各种聚合操作,如过滤、映射、归约等。
Stream API的主要优势在于它提供了一种高级声明式编程模型,极大地简化了集合的操作。
从集合创建Stream
集合是最常见的数据源,所有的Collection
接口的实现类都可以通过stream()
方法获取流。
使用Collection.stream()
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()
方法:
Stream<String> parallelFruitStream = fruits.parallelStream();
parallelStream()
适用于大数据集和多核处理器环境,能够显著提高处理速度。但并不是所有情况下都比顺序流更高效,尤其是对于小数据集。
从数组创建Stream
Java提供了Arrays.stream()
方法来从数组创建流。
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:
Stream<String> partialStream = Arrays.stream(fruitsArray, 1, 3); // 只获取索引1和2的元素
partialStream.forEach(System.out::println);
输出:
Banana
Orange
从值创建Stream
如果你只有几个固定的值,可以使用Stream.of()
方法直接创建流。
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
有时你可能需要创建一个不包含任何元素的空流:
Stream<String> emptyStream = Stream.empty();
使用Stream.builder()
Stream.builder()
方法提供了一个构建器API,用于逐步构建流:
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()
方法可以创建一个无限流,通过对初始值不断应用指定函数生成后续元素:
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 9+
Stream<Integer> finiteStream = Stream.iterate(1, n -> n < 10, n -> n + 2);
finiteStream.forEach(System.out::println);
使用Stream.generate()创建无限流
Stream.generate()
方法接收一个Supplier
函数,不断调用该函数生成元素:
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提供了从文件创建流的方法:
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提供了特殊的流实现以避免装箱/拆箱操作带来的性能开销。
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:
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);
}
}
实际应用案例
处理电子商务网站的产品数据
假设我们有一个电子商务网站,需要处理大量产品信息:
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的方法:
- 从集合创建:
collection.stream()
和collection.parallelStream()
- 从数组创建:
Arrays.stream(array)
- 从值创建:
Stream.of(val1, val2, ...)
- 创建空流:
Stream.empty()
- 使用构建器:
Stream.builder()
- 创建无限流:
Stream.iterate()
和Stream.generate()
- 从文件创建:
Files.lines()
- 从基本类型创建:
IntStream
,LongStream
,DoubleStream
- 从字符串创建:
string.chars()
使用无限流时,务必使用limit()
或者其他终止操作来限制流的大小,否则可能导致程序无法终止。
练习
- 创建一个包含1到10所有偶数的Stream,并计算它们的和。
- 使用Stream.generate()创建一个包含10个随机整数(1-100之间)的Stream,并找出其中的最大值。
- 从一个集合中筛选出所有长度大于5的字符串,并将它们转换为大写形式。
- 使用IntStream创建一个1到20的流,并计算所有能被3整除的数字的平均值。
延伸阅读
掌握Stream的创建是使用Java Stream API的第一步,接下来你可以学习如何使用各种流操作(如filter、map、reduce等)来处理数据。