我想要两个功能。给定Collection时,第一个将返回满足谓词的元素。谓词可能非常昂贵,结果不会完全消耗,所以我认为最好的做法是返回一个视图。因此,一旦实现,我的方法只是封装了Guava的Collections2.filter:
Collection<MyElement> getInterestingElements(Collection<MyElement> allElements) {
return Collections2.filter(allElements, new Predicate<MyElement>() {
@Override
public boolean apply(MyElement element) {
return element.isInteresting(); // call expensive function
}
});
}
(已更改班级名称以保护无辜者)
我的第二个函数将调用第一个函数并返回:
也就是说,当且仅当此集合是单个集合时,此函数才会返回集合的内容。
一个天真的实现将是:
MyElement getElementIfOnlyInterestingOne(Collection<MyElement> allElements) {
Collection<MyElement> interestingElements = getInterestingElements(allElements);
if (interestingElements.size() != 1){
return null;
}
return Iterables.first(interestingElements, null);
}
但是size()
的调用将(我认为)评估底层集合的所有元素的谓词,当我只对第一个元素感兴趣时,这是不可接受的。
我可以使用Iterables.getOnlyElement()但是如果集合不是单例,这会引发异常,这应该经常发生,我认为依靠异常来做这件事是不好的做法。
所以我必须手动迭代,在变量中存储第一个元素,如果有第二个元素则返回null。
我的问题是:这很好,但我不是在重新发明轮子吗?番石榴有很多神奇之处,这个问题必须由某个地方的isSingleton
或getSingleElementOrNull
来解决:)
答案 0 :(得分:10)
“Guava方式”将使用FluentIterable
的firstMatch
方法,该方法返回Optional
个实例。在你的情况下:
MyElement interestingOrNull = FluentIterable.from(allElements)
.firstMatch(new Predicate<MyElement>() {
@Override
public boolean apply(MyElement element) {
return element.isInteresting();
}
})
.orNull();
(更多“番石榴方式”毕竟不会使用null
...)
答案 1 :(得分:7)
如果必须按照描述的方式实现方法,其中包含除一个元素之外的任何其他元素的过滤集合必须返回null
,那么我能提出的最佳解决方案是您已经建议的解决方案。
Collection<MyElement> interestingElements = getInterestingElements(allElements);
Iterator<MyElement> iterator = interestingElements.iterator();
if (!iterator.hasNext()) {
return null;
}
MyElement first = iterator.next();
if (iterator.hasNext()) { // More than one element
return null;
} else {
return first;
}
答案 2 :(得分:0)
当然,上面的FluentIterable建议不涉及尺寸&gt; 2 =&gt;问题的零部分。就个人而言,我会选择Iterables.getOnlyValue方法。它略显凌乱,但它大大简化了代码,意图很明确。