我在Java流操作中遇到了一个边缘案例...
我想编码以下行为:“从任意一篮子水果中,收集最小的20个,除了最小的梨子,因为我们不需要这个。”
额外的奖励:来的篮子可能根本没有任何梨。
例子 :
到目前为止,我在这一步:
output = basket.stream()
.sorted(Comparator.comparing(Fruit::getSize))
//.filter(???)
.limit(20)
.collect(fruitCollector);
这看起来像是一个有状态的 lambda过滤器,我不知道该怎么做。
我不能使用本地firstPear
布尔值,并将其设置为true
过滤第一个梨之后,因为lambda中的所有局部变量都必须是最终的。
最糟糕的情况是,我可以把篮子分成两个,梨子和非梨子,把梨子分类,如果有的话,将它们分类。这看起来非常低效和丑陋。有没有更好的办法?
我组装了一个小型测试工具来比较这些算法的性能。
这个比较没有我想要的那么广泛 - 已经有3周了。它仅涵盖顺序处理简单项目的用法。随意给测试设备一个去,并添加更多的测试,更多的基准或自己的实现。
我的分析:
算法| 作者| Perf | 注释
--------------------------------------------------------------------------------
索引删除| Holger | 最佳| 最好的整体,有点模糊
有状态谓词| pedromss | 最佳| 不要用于并行处理
直截了当的方法| Misha | 最佳| 当少数元素匹配时更好
自定义收集器| 尤金| 好| 当全部或者没有元素匹配时更好
Comaprator hack w / dummy | yegodm | 好| -
比较黑客| xenteros | * | 对输出大小敏感的Perf在边缘情况下失败。
由于它的良好性能和“黑盒子”功能(状态管理代码位于外部类中,贡献者可以专注于业务逻辑),因此我获得了很好的答案,因为它是我们在项目中实施的答案。 )。
请注意,接受的答案可能不是最适合您的:查看其他人,或者查看我的测试项目以亲自查看。
发布于 2018-03-02 08:30:18
你可以使用有状态的谓词:
class StatefulPredicate<T> implements Predicate<T> {
private boolean alreadyFiltered;
private Predicate<T> pred;
public StatefulPredicate(Predicate<T> pred) {
this.pred = pred;
this.alreadyFiltered = false;
}
@Override
public boolean test(T t) {
if(alreadyFiltered) {
return true;
}
boolean result = pred.test(t);
alreadyFiltered = !result;
return result;
}
}
Stream.of(1, -1, 3, -4, -5, 6)
.filter(new StatefulPredicate<>(i -> i > 0))
.forEach(System.out::println);
打印: 1, 3, -4, -5, 6
如果并发是一个问题,您可以使用原子布尔值。
如果您希望跳过多于1个元素,请将该参数添加到您的构造函数中并在其中构建逻辑 StatefulPredicate
此谓词过滤掉第一个负面元素,然后让其他元素通过,无论如何。在你的情况下,你应该测试instanceof Pear
由于人们对过滤器无国籍表示担忧,从文档:
中间业务进一步分为无状态和有状态操作。无状态操作(如过滤器和映射)在处理新元素时不会保留先前看到的元素的状态 - 每个元素都可以独立于其他元素上的操作进行处理。有状态的操作(如独立和排序)可以在处理新元素时结合之前看到的元素的状态。
该谓词不保留有关先前看到的元素的信息。它保留有关以前结果的信息。
也可以使线程安全以避免并发问题。
发布于 2018-03-02 09:48:33
你有没有考虑过简单的方法?找到最小的梨,过滤掉(如果存在)并收集20个最小的梨:
Optional<Fruit> smallestPear = basket.stream()
.filter(Fruit::isPear) // or whatever it takes to test if it's a pear
.min(Fruit::getSize);
Stream<Fruit> withoutSmallestPear = smallestPear
.map(p -> basket.stream().filter(f -> f != p))
.orElseGet(basket::stream);
List<Fruit> result = withoutSmallestPear
.sorted(comparing(Fruit::getSize))
.limit(20)
.collect(toList());
https://stackoverflow.com/questions/-100007479
复制相似问题