面试题答案
一键面试性能优化
- 并行流的使用:
- 适用场景:当数据集足够大且聚合操作计算密集时,使用并行流可利用多核CPU的优势提升性能。例如,对包含数百万条销售记录的列表进行多级分组统计销售额。
- 注意事项:并非所有场景并行流都能提升性能,对于小数据集,并行流的线程创建和管理开销可能大于计算收益。此外,并行流的操作顺序是不确定的,若操作结果依赖顺序,则需谨慎使用。
- 中间操作的惰性求值原理:
- 原理:Stream API的中间操作(如
filter
、map
等)是惰性求值的,它们不会立即执行,而是在终端操作(如collect
、reduce
等)调用时才会执行。这意味着可以将多个中间操作串联起来,避免多次遍历数据集。例如,list.stream().filter(item -> item.getPrice() > 100).map(Item::getCategory).collect(Collectors.toList())
,只有在调用collect
时,filter
和map
操作才会实际执行。 - 优化思路:尽量将复杂的过滤和转换操作通过中间操作串联,减少数据的多次遍历,提升性能。
- 原理:Stream API的中间操作(如
并行流的线程安全问题及解决方法
- 线程安全问题:
- 共享可变状态:并行流中多个线程同时访问和修改共享可变对象时会出现线程安全问题。例如,多个线程同时对一个共享的计数器变量进行自增操作,可能导致结果不准确。
- 有状态的中间操作:某些有状态的中间操作(如
distinct
、sorted
)在并行流中可能会产生意想不到的结果,因为这些操作依赖于流中元素的顺序和状态,而并行流的元素处理顺序是不确定的。
- 解决方法:
- 避免共享可变状态:尽量使用不可变对象或线程安全的可变对象。例如,使用
AtomicInteger
代替普通的Integer
作为共享计数器,AtomicInteger counter = new AtomicInteger();
,在并行流中使用counter.incrementAndGet()
进行安全的自增操作。 - 无状态中间操作:优先选择无状态的中间操作。对于必须使用有状态中间操作的情况,要谨慎评估其在并行流中的行为。例如,在需要去重时,可以先对数据进行分区,在每个分区内去重,最后合并结果,而不是直接在并行流中使用
distinct
。
- 避免共享可变状态:尽量使用不可变对象或线程安全的可变对象。例如,使用