面试题答案
一键面试Java Stream API底层实现机制
- 数据源:Stream 可以从多种数据源获取数据,如集合、数组、I/O 通道等。数据源为 Stream 提供数据序列。
- 中间操作:
- 中间操作返回一个新的 Stream,且是惰性求值的。例如
filter
、map
等操作,它们不会立即执行,而是将操作记录下来。 - 底层实现时,中间操作会构建一个操作链,这些操作会在终端操作触发时依次执行。
- 中间操作返回一个新的 Stream,且是惰性求值的。例如
- 终端操作:
- 终端操作会触发整个 Stream 操作链的执行,是及早求值的。例如
forEach
、collect
等操作。 - 执行时,会从数据源开始,依次执行中间操作链,最后执行终端操作,并返回结果。
- 终端操作会触发整个 Stream 操作链的执行,是及早求值的。例如
在复杂业务场景下的深度优化
- 避免重复计算 - 中间结果复用:
- 使用
peek
操作在不改变流元素的情况下记录中间结果。例如,如果有多个map
和filter
操作,且某些中间结果需要复用,可以在合适位置使用peek
。 - 示例代码:
- 使用
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
AtomicReference<List<Integer>> intermediateResult = new AtomicReference<>();
List<Integer> result = numbers.stream()
.peek(list -> intermediateResult.set(new ArrayList<>(list)))
.map(n -> n * 2)
.filter(n -> n > 5)
.collect(Collectors.toList());
// 这里可以复用 intermediateResult 中的数据
- 还可以使用 `collect` 操作提前收集中间结果,然后在后续操作中复用。例如,先收集成 `List`,再对这个 `List` 进行其他 Stream 操作。
2. 自定义收集器:
- 实现 Collector
接口,该接口有五个方法:supplier
、accumulator
、combiner
、finisher
和 characteristics
。
- 统计元素出现频率并按频率降序排列:
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class FrequencyCollector<T> implements Collector<T, Map<T, Integer>, Map<T, Integer>> {
@Override
public Supplier<Map<T, Integer>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<T, Integer>, T> accumulator() {
return (map, element) -> map.put(element, map.getOrDefault(element, 0) + 1);
}
@Override
public BinaryOperator<Map<T, Integer>> combiner() {
return (map1, map2) -> {
map2.forEach((key, value) -> map1.put(key, map1.getOrDefault(key, 0) + value));
return map1;
};
}
@Override
public Function<Map<T, Integer>, Map<T, Integer>> finisher() {
return map -> map.entrySet().stream()
.sorted(Map.Entry.<T, Integer>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new
));
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
}
}
- 使用自定义收集器示例:
List<String> words = Arrays.asList("apple", "banana", "apple", "cherry", "banana");
Map<String, Integer> frequencyMap = words.stream().collect(new FrequencyCollector<>());
System.out.println(frequencyMap);
通过以上方式,可以深入理解并优化 Java Stream API 在复杂业务场景中的应用。