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.filter
、Sets.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<T>() @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 加载数据时无法获取工件?