MST
星途 面试题库

面试题:Java Stream多方法组合中的状态共享与副作用问题

假设有一个复杂的业务场景,在Java Stream多方法组合处理数据时,需要在不同的中间操作之间共享一些状态信息(例如统计特定元素出现的次数),同时要避免产生副作用影响流操作的正确性和可重复性。请设计一个方案,使用Java Stream多方法组合完成数据处理,并解决状态共享与副作用的问题,说明设计思路和具体实现方式,以及如何保证线程安全(如果涉及多线程场景)。
22.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 状态共享:使用AtomicInteger等原子类来保存需要共享的状态信息,因为原子类提供了线程安全的操作,避免在多线程环境下的数据竞争。
  2. 避免副作用:将状态更新操作封装在mapfilter等中间操作的函数式接口实现中,确保每个操作都是纯函数,即相同的输入始终产生相同的输出,且不产生可观察的副作用。
  3. 线程安全:在多线程场景下,使用原子类以及并行流时注意流操作本身的线程安全性。并行流会自动利用多核处理器并行处理数据,原子类可以保证共享状态的线程安全更新。

具体实现方式

假设我们有一个List<String>,要统计其中特定字符串(例如"target")出现的次数。

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class StreamStateSharing {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("target", "other", "target", "yetAnother");
        AtomicInteger count = new AtomicInteger(0);

        list.stream()
          .map(s -> {
                if ("target".equals(s)) {
                    count.incrementAndGet();
                }
                return s;
            })
          .filter(s -> !"target".equals(s))
          .forEach(System.out::println);

        System.out.println("Target count: " + count.get());
    }
}

在上述代码中:

  1. 我们创建了一个AtomicInteger实例count来统计特定字符串出现的次数。
  2. map操作中,当遇到特定字符串时,使用AtomicIntegerincrementAndGet方法安全地增加计数。
  3. 后续的filter操作和forEach操作不受状态更新的影响,保证了流操作的正确性和可重复性。

多线程场景下保证线程安全

如果使用并行流,代码如下:

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ParallelStreamStateSharing {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("target", "other", "target", "yetAnother");
        AtomicInteger count = new AtomicInteger(0);

        list.parallelStream()
          .map(s -> {
                if ("target".equals(s)) {
                    count.incrementAndGet();
                }
                return s;
            })
          .filter(s -> !"target".equals(s))
          .forEach(System.out::println);

        System.out.println("Target count: " + count.get());
    }
}

并行流会自动并行处理数据,而AtomicInteger的原子操作保证了count在多线程环境下的安全更新。每个线程都可以独立地更新count,不会产生数据竞争问题。