番石榴:为什么没有Lists.filter()函数?


86

是否有原因

Lists.transform()

但不是

Lists.filter()

如何正确过滤列表?我可以用

new ArrayList(Collection2.filter())

当然可以,但是如果我理解正确的话,就不能保证我的订购顺序保持不变。


8
仅供参考,List.newArrayList(Iterables.filter(...))通常比新的ArrayList(Collection2.filter(...))更快。ArrayList构造函数在过滤的集合上调用size(),并且计算大小要求将过滤器应用于原始列表的每个元素。
贾里德·利维

4
@JaredLevy也许List.newArrayList(Iterables.filter(...))应该代替它Lists.newArrayList(Iterables.filter(...))
Abdull

Answers:


57

它没有实现,因为它将在返回的List视图上暴露出大量危险的慢速方法,例如#get(index)(会引发性能错误)。而且ListIterator也很难实现(尽管我几年前提交了一个补丁来解决)。

由于索引方法在过滤后的列表视图中效率不高,因此最好使用不包含它们的过滤后的Iterable。


7
您假设将返回列表视图。但是,#filter可以实现为返回一个新的物化列表,这实际上是我期望的用于列表的过滤方法,而不是Iterable上的方法。
菲利克斯·莱波德

@FelixLeipold但是这会使水变得混乱。实际上,filter一致表示一个视图(以及暗示的行为)是否为Iterables.filterSets.filter等等。由于Iterables.filter可以轻松地与copyOf任何视图结合使用ImmutableCollection,因此我认为这是一个不错的设计折衷(与提出其他方法和名称(例如filteredCopy,诸如此类)无关,用于简单实用程序的组合)。
路加·乌瑟伍德

37

您可以使用Iterables.filter,它肯定会保持排序。

请注意,通过构造一个新列表,您将复制元素(当然只是引用)-这样就不会在原始列表上显示实时视图。创建视图非常棘手-考虑以下情况:

Predicate<StringBuilder> predicate = 
    /* predicate returning whether the builder is empty */
List<StringBuilder> builders = Lists.newArrayList();
List<StringBuilder> view = Lists.filter(builders, predicate);

for (int i = 0; i < 10000; i++) {
    builders.add(new StringBuilder());
}
builders.get(8000).append("bar");

StringBuilder firstNonEmpty = view.get(0);

那将不得不遍历整个原始列表,将过滤器应用于所有内容。我想这可能要求谓词匹配在视图的整个生命周期中都不会发生变化,但这并不能完全令人满意。

(请注意,这只是猜测,也许是Guava维护者之一会加入真正的原因:)


1
Collections2.filter.iterator只是调用Iterables.filter,所以结果是相同的。
skaffman 2011年

@skaffman:在这种情况下Iterables.filter,为清楚起见,我将使用该版本。
乔恩·斯基特

3
...除非您view.size()稍后在代码中需要一个地方:)
Xaerxess 2011年

28

new List(Collection2.filter())当然可以使用,但是这种方式不能保证我的订购保持不变。

这不是真的 Collections2.filter()是一个延迟评估的函数-它实际上不会过滤您的集合,直到您开始访问过滤的版本。例如,如果您遍历过滤后的版本,则过滤后的元素将以与原始集合相同的顺序从迭代器中弹出(显然要减去过滤出的元素)。

也许您认为它先进行了过滤,然后将结果转储到某种形式的任意,无序的Collection中,但是没有。

所以,如果你使用的输出Collections2.filter()作为输入到一个新的列表,那么你的原始顺序将会被保留。

使用静态导入(和Lists.newArrayList函数),它变得非常简洁:

List filteredList = newArrayList(filter(originalList, predicate));

请注意,虽然Collections2.filter不会急于遍历基础集合,但Lists.newArrayList -它将提取已过滤集合的所有元素并将其复制到new中ArrayList


更像是:List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));或。List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Xaerxess 2011年

@Xaerxess:糟糕,是的,忘记了谓词...已修正
skaffman 2011年

@Bozho:谢谢...花了我足够长的时间:)
skaffman 2011年

12

正如乔恩(Jon)所述,您可以使用Iterables.filter(..)Collections2.filter(..),如果不需要实时取景,则可以使用ImmutableList.copyOf(Iterables.filter(..))或,Lists.newArrayList( Iterables.filter(..))并且将保持顺序。

如果您真的对为什么要购买感兴趣,可以访问https://github.com/google/guava/issues/505了解更多信息。


6

总结其他人说的话,您可以轻松创建通用包装器来过滤列表:

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.