深入理解 Java 8 Stream API

Java 8 引入了 Stream API,使得处理集合数据更加高效和灵活。本文将通过一系列示例代码,深入分析 Stream API 的各个特性和用法,涵盖从流的创建到中间操作、终端操作的全面应用。

1. 创建流

1.1 连续整数流

IntStream intStream = IntStream.rangeClosed(1, 8);
intStream.forEach(System.out::println);

使用 IntStream.rangeClosed 方法生成一个包含 1 到 8 的整数流,并打印每个整数。rangeClosed 方法包括结束值,因此生成的流包含 1 到 8 的所有整数。这种方式非常适合需要处理连续整数的场景。

1.2 随机数流

new Random().ints(5).forEach(System.out::println);

这里生成了一个包含 5 个随机整数的流,并打印出来。ints 方法可以生成无限流,因此可以通过其他方法限制数量。这在需要随机数据时非常有用。

1.3 数组创建流

String[] array = {"1", "2", "3"};
Stream<String> arrayStream = Arrays.stream(array);

通过 Arrays.stream 方法,可以直接从数组创建流。这种方式非常方便,尤其是在处理数组数据时。

1.4 直接创建流

Stream<Serializable> directStream = Stream.of(1, "a", 3, new ArrayList<>());
directStream.forEach(System.out::println);

Stream.of 方法允许我们直接创建流,可以包含不同类型的元素。这对于处理混合类型的数据很实用。

1.5 流的连接

Stream<Serializable> directStream1 = Stream.of(1, "a", 3, new ArrayList<>());
Stream<Serializable> directStream2 = Stream.of(1, "a", 3, new ArrayList<>());
Stream<Serializable> concatStream = Stream.concat(directStream1, directStream2);

使用 Stream.concat 方法可以将两个流合并为一个流,这对于处理多个数据源非常有用。

1.6 动态构建流

Stream.Builder<Object> streamBuilder = Stream.builder();
streamBuilder.add("a").add("b").add("c");
// build之后就不能再添加元素,否则报异常
Stream<Object> buildStream = streamBuilder.build();
buildStream.forEach(System.out::println);

Stream.Builder 允许我们动态添加元素并构建流,适合在运行时决定流的内容。

1.7 从文件创建流

Path path = Paths.get("C:\\JavaProjects\\tomato-demo\\demo-tomato-micro\\src\\main\\resources\\file.txt");
try (Stream<String> lines = Files.lines(path)) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}

通过 Files.lines 方法可以从文件中逐行读取数据并创建流,适合处理文本文件。

1.8 无限流

Stream<String> constStream = Stream.generate(() -> "Albert").limit(50);
constStream.forEach(System.out::println);

Stream.generate 方法生成无限流,通过 limit 方法限制数量,适合需要重复元素的情况。

1.9 随机数流

Stream.generate(Math::random).limit(5).forEach(System.out::println);

这里生成了 5 个随机数的流,展示了如何使用 generate 方法结合 Math::random

1.10 步进流

Stream<Integer> iterateStream = Stream.iterate(0, count -> count + 1).limit(500);
iterateStream.forEach(System.out::println);

Stream.iterate 方法用于生成步进流,适合需要根据某种规则生成序列的情况。

1.11 并行流

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("8");
list.add("4");
Stream<String> parallelStream = list.parallelStream();
parallelStream.forEach(System.out::println);

通过 parallelStream 方法可以创建并行流,利用多核处理器提高处理效率。并行流可以显著提高大数据量处理的性能。

2. 中间操作

2.1 过滤和去重

List<User> users = Stream.of(
    new User("周润发", 18, "中国"),
    new User("亚索", 30, "小日子"),
    new User("贾冰", 49, "中国"),
    new User("周润发", 18, "中国")
).collect(Collectors.toList());

users.stream().filter(user -> user.getAge() > 18).forEach(System.out::println);
System.out.println("去重");
users.stream().distinct().limit(2).skip(1).forEach(System.out::println);

使用 filter 方法可以根据条件过滤元素,distinct 方法用于去重。注意,自定义类需要重写 equalshashCode 方法,以确保去重的正确性。

2.2 映射

Stream<String> stringStream = users.stream().map(User::getName);
stringStream.forEach(System.out::println);

map 方法用于将流中的每个元素转换为另一种形式,这里将 User 对象映射为其名字,方便后续处理。

2.3 扁平化流

List<User> users1 = Stream.of(
    new User("周润发1", 18, "中国"),
    new User("亚索1", 30, "小日子"),
    new User("贾冰1", 49, "中国"),
    new User("周润发1", 18, "中国")
).collect(Collectors.toList());
List<User> users2 = Stream.of(
    new User("周润发2", 18, "中国"),
    new User("亚索2", 30, "小日子"),
    new User("贾冰2", 49, "中国"),
    new User("周润发2", 18, "中国")
).collect(Collectors.toList());
List<User> users3 = Stream.of(
    new User("周润发3", 18, "中国"),
    new User("亚索3", 30, "小日子"),
    new User("贾冰3", 49, "中国"),
    new User("周润发3", 18, "中国")
).collect(Collectors.toList());

System.out.println("双层List扁平化");
Stream<List<User>> usersStream = Stream.of(users1, users2, users3);
Stream<User> userStream = usersStream.flatMap(List<User>::stream);
userStream.map(User::getName).forEach(System.out::println);

flatMap 方法用于将嵌套流扁平化,适合处理多层数据结构,简化数据处理流程。

2.4 排序

System.out.println("按名字长度排序");
users.stream()
    .sorted(Comparator.comparingInt((User user) -> user.getName().length()).reversed())
    .forEach(System.out::println);

System.out.println("按年龄排序");
users.stream()
    .sorted(Comparator.comparingInt(User::getAge))
    .forEach(System.out::println);

System.out.println("按年龄倒序");
users.stream()
    .sorted(Comparator.comparingInt(User::getAge).reversed())
    .forEach(System.out::println);

使用 sorted 方法可以对流中的元素进行排序,支持自定义比较器。这里展示了按名字长度和年龄的排序方式。

3. 查找与匹配

3.1 短路操作

boolean anyMatchResult = users.stream().anyMatch(user -> user.getAge() == 18);
System.out.println("anyMatch结果是" + anyMatchResult);

boolean noneMatchResult = users.stream().noneMatch(user -> user.getAge() == 18);
System.out.println("noneMatch结果是" + noneMatchResult);

boolean allMatchResult = users.stream().allMatch(user -> user.getAge() > 0);
System.out.println("allMatch结果是" + allMatchResult);

anyMatchnoneMatchallMatch 方法用于检查流中是否有元素满足某个条件,适合快速判断。

3.2 查找

Optional<User> findFirstOptionalUser = users.stream().findFirst();
System.out.println("findFirst结果是" + findFirstOptionalUser.get());
findFirstOptionalUser.ifPresent(System.out::println);

Optional<User> findAnyOptionalUser = users.stream().findAny();
System.out.println("findAny结果是" + findAnyOptionalUser);
findAnyOptionalUser.ifPresent(System.out::println);

findFirstfindAny 方法用于查找流中的元素,返回 Optional 对象以处理可能为空的情况。

4. 聚合操作

4.1 统计

System.out.println("元素数量" + users.stream().count());

count 方法用于统计流中元素的数量。

4.2 最大值和最小值

Optional<User> max = users.stream().max(Comparator.comparingInt(User::getAge));
System.out.println("最大值");
max.ifPresent(System.out::println);

Optional<User> min = users.stream().min(Comparator.comparingInt(User::getAge));
System.out.println("最小值");
min.ifPresent(System.out::println);

maxmin 方法用于查找流中的最大值和最小值。

4.3 求和和平均值

IntStream userIntStreamSum = users.stream().mapToInt(User::getAge);
System.out.println("求和" + userIntStreamSum.sum());

IntStream userIntStreamAver = users.stream().mapToInt(User::getAge);
System.out.println("求平均值" + userIntStreamAver.average());

IntStream ageStream = users.stream().mapToInt(User::getAge);
int sum = ageStream.reduce(0, (a, b) -> a + b);
System.out.println("reduce求和" + sum);

使用 mapToInt 方法将流中的元素转换为 IntStream,然后可以进行求和、求平均等操作。reduce 方法用于将流中的元素合并为一个结果,这里用于求和。

4.4 字符串拼接

String joinName = users.stream().map(User::getName).reduce("", (a, b) -> a + b + " ");
System.out.println("字符串拼接" + joinName);

reduce 方法用于将流中的元素合并为一个结果,这里用于字符串拼接。

4.5 收集

System.out.println("收集" + users.stream().map(User::getCountry).collect(Collectors.toList()));

Map<String, Integer> name_age = users.stream().distinct().collect(Collectors.toMap(User::getName, User::getAge));
System.out.println("名字-年龄Map" + name_age);

collect 方法用于将流中的元素收集到集合中,支持多种收集器,如 toListtoMap 等。这里展示了如何收集国家信息和生成名字-年龄的映射。

5. 分组与分区

5.1 分组

Map<String, List<User>> countryGroup = users.stream().collect(Collectors.groupingBy(User::getCountry));
System.out.println("分组" + countryGroup);

groupingBy 方法用于根据某个属性将流中的元素分组,适合统计和分析。

5.2 分区

Map<Boolean, List<User>> partitioningBy = users.stream().collect(Collectors.partitioningBy(user -> user.getAge() > 18));
System.out.println(partitioningBy);

partitioningBy 方法根据条件将流中的元素分为两个部分,返回一个 Map

5.3 字符串连接

String join = users.stream().map(User::getName).collect(Collectors.joining(","));
System.out.println(join);

使用 Collectors.joining 方法可以将流中的字符串连接成一个结果字符串,方便输出。

5.4 统计

IntSummaryStatistics ageSummarizingInt = users.stream().collect(Collectors.summarizingInt(User::getAge));
System.out.println("求和" + ageSummarizingInt.getSum());
System.out.println("最大值" + ageSummarizingInt.getMax());
System.out.println("平均值" + ageSummarizingInt.getAverage());
System.out.println("数量" + ageSummarizingInt.getCount());
System.out.println("最小值" + ageSummarizingInt.getMin());

Collectors.summarizingInt 方法可以快速获取流中元素的统计信息,如求和、最大值、最小值等。

6. 并行流

6.1 并行处理

List<String> parallelList = new ArrayList<>();
parallelList.add("A");
parallelList.add("P");
parallelList.add("P");
parallelList.add("L");
parallelList.add("E");
parallelList.add("S");

// 这样的foreach不能保证元素顺序
System.out.println("不按顺序来");
parallelList.parallelStream().map(String::toLowerCase).forEach(System.out::println);

// foreach order可以保证原始顺序
System.out.println("按顺序来");
parallelList.parallelStream().map(String::toLowerCase).forEachOrdered(System.out::println);

并行流可以提高处理效率,但要注意输出顺序可能不一致。使用 forEachOrdered 可以保证顺序。

6.2 并行流的线程

System.out.println("不按顺序来");
parallelList.parallelStream().map(String::toLowerCase).forEach(item -> {
    System.out.println("item: " + item + " Thread ->" + Thread.currentThread().getName());
});

以查看并行流中每个元素处理时所在线程的名称,帮助理解并行处理的工作方式。

6.3 并行流数据收集的顺序

List<String> parallelCollect = parallelList.parallelStream().map(String::toLowerCase).collect(Collectors.toList());
System.out.println("并行流收集数据问题" + parallelCollect);

并行流在数据收集时也会保持顺序,这对于需要顺序输出的场景非常重要。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

服务端相声演员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值