如何在Java Stream中按需延迟调用开销高昂的方法?
问题
有一个开销高昂的方法expensive(),希望仅在必要时在Stream中调用。需求是:根据input值从myList中查找目标MyObject,仅当myList中不存在该对象时,才调用expensive()获取更大的列表并从中查找。
原使用Stream.concat的实现代码如下:
public static Optional<MyObject> findTarget(String input, List<MyObject> myList) { return Stream.concat(myList.stream(), expensive().stream()).filter(o -> o.hasName(input)).findFirst(); }
但该方案无法满足需求,因为Stream.concat会在遍历完myList之前就调用expensive()。
现有一个两步实现方案,但写法会重复filter等逻辑,不够优雅:
return myList.stream().filter(o -> o.hasName(input)).findFirst().or( () -> expensive().stream().filter(o -> o.hasName(input)).findFirst());
请问是否有更优的解决方案,甚至是单行的Stream实现方式?
更优解决方案
1. 提取重复逻辑,消除代码冗余
把过滤条件提取为独立的Predicate,既能避免重复编写过滤逻辑,又保持了延迟调用expensive()的特性,是可读性和简洁性平衡最好的方案:
public static Optional<MyObject> findTarget(String input, List<MyObject> myList) { Predicate<MyObject> matchesInput = o -> o.hasName(input); return myList.stream().filter(matchesInput).findFirst() .or(() -> expensive().stream().filter(matchesInput).findFirst()); }
2. 单行延迟加载Stream实现
如果追求单行写法,可以利用Stream.of结合Supplier实现延迟触发expensive(),只有当第一个Stream遍历完无结果时,才会执行第二个Stream的生成逻辑:
public static Optional<MyObject> findTarget(String input, List<MyObject> myList) { return Stream.<Object>of(myList.stream(), (Supplier<Stream<MyObject>>) () -> expensive().stream()) .flatMap(s -> s instanceof Supplier ? ((Supplier<Stream<MyObject>>) s).get() : (Stream<MyObject>) s) .filter(o -> o.hasName(input)) .findFirst(); }
注意:这种写法可读性稍弱,适合对代码行数有要求的场景。
3. 自定义延迟拼接Stream(高性能场景)
通过StreamSupport和自定义Iterator实现完全的延迟加载,确保expensive()仅在myList遍历完成且无匹配项时才被调用,适合对性能要求极高的场景:
public static Optional<MyObject> findTarget(String input, List<MyObject> myList) { Stream<MyObject> lazyCombinedStream = StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<>() { Iterator<MyObject> currentIterator = myList.iterator(); boolean switchedToExpensive = false; @Override public boolean hasNext() { if (currentIterator.hasNext()) return true; if (!switchedToExpensive) { currentIterator = expensive().iterator(); switchedToExpensive = true; return currentIterator.hasNext(); } return false; } @Override public MyObject next() { return currentIterator.next(); } }, Spliterator.ORDERED ), false ); return lazyCombinedStream.filter(o -> o.hasName(input)).findFirst(); }
推荐方案
优先选择提取Predicate的方案,它在代码简洁性、可读性和性能之间达成了最优平衡,既解决了代码冗余问题,又严格保证了expensive()的按需调用。
内容的提问来源于stack exchange,提问作者user1589188
相关产品推荐
相关产品推荐

