Java Streams 中的7个常见错误

在使用 Java Streams 时,以下是一些常见的错误:

1.不使用终止操作

错误 :忘记调用终止操作(如collect()forEach()reduce()),这会导致流没有执行。

typescript 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

    // 创建流但没有调用终止操作
    names.stream()
    .filter(name -> name.startsWith("A")); // 这里没有调用终止操作

    // 由于流没有执行,什么都不会打印
    System.out.println("Stream operations have not been executed.");
}

解决方案:始终以终止操作结束,以触发流的处理。

arduino 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

    // 创建流并调用终止操作
    names.stream()
    .filter(name -> name.startsWith("A")) // 中间操作
    .forEach(System.out::println); // 终止操作

    // 这将打印 "Alice",因为流被执行了
}

2.修改源数据

错误 :在处理流时修改源数据结构(如List)可能导致未知的结果。

arduino 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    // 尝试在流处理时修改源列表
    names.stream()
    .filter(name -> {
        if (name.startsWith("B")) {
            names.remove(name); // 修改源列表
        }
        return true;
    })
    .forEach(System.out::println);
    // 由于并发修改,输出可能不符合预期
    System.out.println("Remaining names: " + names);
}

解决方案:不要在流操作期间修改源数据,而是使用流创建新的集合。

typescript 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    // 基于过滤结果创建一个新列表
    List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("B")) // 过滤出以 'B' 开头的名字
    .collect(Collectors.toList());
    // 显示过滤后的列表
    System.out.println("Filtered names: " + filteredNames);
    System.out.println("Original names remain unchanged: " + names);
}

3.忽略并行流的开销

错误:认为并行流总是能提高性能,而不考虑上下文,例如小数据集或轻量级操作。

arduino 复制代码
public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 小数据集
    // 在小数据集上使用并行流
    numbers.parallelStream()
    .map(n -> {
        // 模拟轻量级操作
        System.out.println(Thread.currentThread().getName() + " processing: " + n);
        return n * n;
    })
    .forEach(System.out::println);
    // 输出可能显示为简单任务创建了不必要的线程
}

解决方案:谨慎使用并行流,尤其是对于大数据集的 CPU 密集型任务。

scss 复制代码
public static void main(String[] args) {
    List<Integer> numbers = IntStream.rangeClosed(1, 1_000_000) // 大数据集
    .boxed()
    .collect(Collectors.toList());
    // 在大数据集上使用并行流进行 CPU 密集型操作
    List<Integer> squareNumbers = numbers.parallelStream()
    .map(n -> {
        // 模拟 CPU 密集型操作
        return n * n;
    })
    .collect(Collectors.toList());
    // 打印前 10 个结果
    System.out.println("First 10 squared numbers: " + squareNumbers.subList(0, 10));
}

4.过度使用中间操作

错误 :链式调用过多的中间操作(如filter()map())可能会引入性能开销。

rust 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");

    // 过度使用中间操作
    List<String> result = names.stream()
    .filter(name -> name.startsWith("A")) // 第一个中间操作
    .filter(name -> name.length() > 3) // 第二个中间操作
    .map(String::toUpperCase) // 第三个中间操作
    .map(name -> name + " is a name") // 第四个中间操作
    .toList(); // 终端操作

    // 输出结果
    System.out.println(result);
}

解决方案:尽量减少流管道中的中间操作,并在可能的情况下使用流融合。

typescript 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");

    // 优化流管道
    List<String> result = names.stream()
    .filter(name -> name.startsWith("A") && name.length() > 3) // 将过滤器合并为一个
    .map(name -> name.toUpperCase() + " is a name") // 合并 map 操作
    .toList(); // 终端操作

    // 输出结果
    System.out.println(result);
}

5.不处理 Optional 值

错误 :在使用findFirst()reduce()等操作时,没有正确处理Optional结果。

typescript 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    // 尝试查找以 "Z" 开头的名字(不存在)
    String firstNameStartingWithZ = names.stream()
    .filter(name -> name.startsWith("Z")) 
    .findFirst() // 返回一个 Optional
    .get(); // 如果 Optional 为空,这将抛出 NoSuchElementException
    // 输出结果
    System.out.println(firstNameStartingWithZ);
}

解决方案 :在访问Optional的值之前,始终检查它是否存在,以避免NoSuchElementException

typescript 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    // 正确处理 Optional
    Optional<String> firstNameStartingWithZ = names.stream()
    .filter(name -> name.startsWith("Z")) 
    .findFirst(); // 返回一个 Optional
    // 检查 Optional 是否存在
    if (firstNameStartingWithZ.isPresent()) {
        System.out.println(firstNameStartingWithZ.get());
    } else {
        System.out.println("No name starts with 'Z'");
    }
}

6.忽略线程安全

错误:在并行流中使用共享的可变状态可能导致竞态条件和不一致的结果。

typescript 复制代码
public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    List<Integer> results = new ArrayList<>(); // 共享的可变状态
    // 在并行流中使用共享的可变状态
    numbers.parallelStream().forEach(number -> {
        results.add(number * 2); // 这可能导致竞态条件
    });
    // 输出结果
    System.out.println("Results: " + results);
}

解决方案:避免共享可变状态;使用线程安全的集合或局部变量。

typescript 复制代码
public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    List<Integer> results = new CopyOnWriteArrayList<>(); // 线程安全的集合
    // 在并行流中使用线程安全的集合
    numbers.parallelStream().forEach(number -> {
        results.add(number * 2); // 避免竞态条件
    });
    // 输出结果
    System.out.println("Results: " + results);
}

7.混淆中间操作和终止操作

错误:不清楚中间操作(返回新流)和终止操作(产生结果)之间的区别。

arduino 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    // 错误:尝试将中间操作用作终止操作
    // 这将无法编译,因为 'filter' 返回一个 Stream,而不是一个 List
    names.stream().filter(name -> name.startsWith("A")).forEach(System.out::println); // 这里正确使用了终止操作
}

解决方案:熟悉每种操作类型的特性,以避免代码中的逻辑错误。

typescript 复制代码
public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    // 正确使用中间操作和终止操作
    List<String> filteredNames = names.stream()
        .filter(name -> name.startsWith("A")) // 中间操作
        .collect(Collectors.toList()); // 终止操作

    // 输出过滤后的名字
    System.out.println("Filtered Names: " + filteredNames);
}

通过掌握这些技巧并实施这些解决方案,你可以更好地使用 Java Streams,并编写更简洁、更高效的代码。

相关推荐
程序员爱钓鱼11 小时前
Go语言实战案例 — 工具开发篇:实现一个图片批量压缩工具
后端·google·go
程序员的世界你不懂11 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
星空寻流年11 小时前
设计模式第一章(建造者模式)
java·设计模式·建造者模式
gb421528712 小时前
java中将租户ID包装为JSQLParser的StringValue表达式对象,JSQLParser指的是?
java·开发语言·python
曾经的三心草12 小时前
Python2-工具安装使用-anaconda-jupyter-PyCharm-Matplotlib
android·java·服务器
Metaphor69212 小时前
Java 高效处理 Word 文档:查找并替换文本的全面指南
java·经验分享·word
ChinaRainbowSea13 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
舒一笑13 小时前
同步框架与底层消费机制解决方案梳理
后端·程序员
stormsha13 小时前
飞算JavaAI炫技赛电商系统商品管理模块的架构设计与实现
java·架构·鸿蒙系统
minh_coo13 小时前
Spring框架事件驱动架构核心注解之@EventListener
java·后端·spring·架构·intellij-idea