Guava:为啥没有 Lists.filter() 函数?

Posted

技术标签:

【中文标题】Guava:为啥没有 Lists.filter() 函数?【英文标题】:Guava: Why is there no Lists.filter() function?Guava:为什么没有 Lists.filter() 函数? 【发布时间】:2012-01-17 12:33:09 【问题描述】:

有什么原因吗

Lists.transform()

但没有

Lists.filter()

?

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

new ArrayList(Collection2.filter())

当然可以,但是如果我理解正确,这样就不能保证我的订单保持不变。

【问题讨论】:

仅供参考,List.newArrayList(Iterables.filter(...)) 通常比 new ArrayList(Collection2.filter(...)) 快。 ArrayList 构造函数在过滤后的集合上调用 size(),计算大小需要将过滤器应用于原始列表的每个元素。 @JaredLevy 也许应该说List.newArrayList(Iterables.filter(...)) 而不是Lists.newArrayList(Iterables.filter(...)) 【参考方案1】:

没有实现它是因为它会暴露大量危险的慢方法,例如返回的列表视图上的#get(index)(引起性能错误)。而且 ListIterator 实施起来也很痛苦(尽管我在几年前提交了一个 patch 来涵盖这一点)。

由于索引方法在过滤后的 List 视图中效率不高,最好只使用过滤后的 Iterable,它没有它们。

【讨论】:

您假设将返回一个列表视图。但是,#filter 可以实现为返回一个新的物化列表,这实际上是我对列表的过滤器方法的期望,而不是 Iterable 上的过滤器方法。 @FelixLeipold 然而,这会使水变得浑浊。事实上,filter 始终表示一个视图(以及暗示的行为),无论是Iterables.filterSets.filter 等。由于Iterables.filter 很容易与任何ImmutableCollection 上的copyOf 结合,我发现这是一个良好的设计权衡(相对于提出额外的方法和名称,如filteredCopy 或诸如此类,用于简单实用程序的组合)。【参考方案2】:

您可以使用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 维护者之一会以真正的原因参与进来:)

【讨论】:

Collections2.filter.iterator 只是调用Iterables.filter,所以结果是一样的。 @skaffman:在这种情况下,为了清楚起见,我会使用Iterables.filter 版本。 ...除非您在代码后面的某处需要view.size() :)【参考方案3】:

我当然可以使用new List(Collection2.filter()),但这样不能保证我的订单保持不变。

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

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

因此,如果您使用Collections2.filter() 的输出作为新列表的输入,那么您的原始订单保留。

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

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

请注意,虽然Collections2.filter 不会急切地迭代基础集合,但Lists.newArrayList - 它会提取过滤集合的所有元素并将它们复制到新的ArrayList 中。

【讨论】:

更像是:List filteredList = newArrayList(filter(originalList, new Predicate&lt;T&gt;() @Override public boolean apply(T input) return (...); )); 或 ie。 List filteredList = newArrayList(filter(originalList, Predicates.notNull())); @Xaerxess:哎呀,是的,忘记谓词了……已修复 @Bozho:谢谢...花了我足够长的时间:)【参考方案4】:

正如 Jon 所提到的,您可以使用 Iterables.filter(..)Collections2.filter(..),如果您不需要实时取景,您可以使用 ImmutableList.copyOf(Iterables.filter(..))Lists.newArrayList( Iterables.filter(..)),并且可以保持订购。 如果您真的对为什么部分感兴趣,可以访问https://github.com/google/guava/issues/505了解更多详情。

【讨论】:

【参考方案5】:

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

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) 
    return Lists.newArrayList(Iterables.filter(userLists, predicate));

【讨论】:

以上是关于Guava:为啥没有 Lists.filter() 函数?的主要内容,如果未能解决你的问题,请参考以下文章

java导入了guava,但是运行时报没有找到该类,是哪里没有配置好吗?

有没有办法使用 Guava 获取 InputStream 的哈希码?

IllegalStateException:没有为 Guava 12.0 加载数据时无法获取工件?

有没有一种优雅的方法可以在使用 Guava 转换集合时删除空值?

Guava学习笔记概览

01-guava简介