面试题答案
一键面试问题现象
- 抛出异常:当复用已终止的
Stream
流时,会抛出IllegalStateException
。因为Stream
流的操作是一次性的,一旦执行了终止操作(如collect
、count
、findFirst
等),流就被认为已消耗,不能再次使用。例如:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
int sum = stream.mapToInt(Integer::intValue).sum(); // 第一次终止操作
int count = stream.count(); // 复用已终止的流,会抛出IllegalStateException
- 数据一致性问题:从逻辑角度看,如果允许复用已终止的流,可能会导致数据处理逻辑混乱。因为终止操作可能已经改变了流的内部状态,如果再次使用,可能不会按照预期的方式处理数据,导致计算结果不准确。
正确解决方案
- 重新创建流:每次需要进行聚合操作时,重新从数据源创建新的
Stream
流。例如:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算总和
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
// 统计数量
long count = numbers.stream().count();
- 使用中间操作的结果:如果某些中间操作的结果可以复用,可以先将中间操作的结果存储起来,然后基于这个结果进行多次终止操作。例如:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
IntStream intStream = stream.mapToInt(Integer::intValue);
int sum = intStream.sum();
intStream = numbers.stream().mapToInt(Integer::intValue);
long count = intStream.count();
这种方式虽然多了一步创建 IntStream
的操作,但避免了重新从数据源创建流的开销,适用于数据源创建流开销较大的情况。不过要注意每次终止操作后,需要重新获取 IntStream
,因为 IntStream
也是一次性使用的。