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

时间:2011-12-10 18:01:40

标签: java list filter guava

有没有理由

Lists.transform()

但没有

Lists.filter()

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

new ArrayList(Collection2.filter())

当然,但是如果我理解的话,这种方式并不能保证我的订单保持不变。

5 个答案:

答案 0 :(得分:57)

它没有实现,因为它会在返回的List视图上显示危险的大量慢速方法,例如#get(index)(引发性能错误)。并且ListIterator也很难实现(尽管我在patch年前提交了一份文章来解决这个问题。)

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

答案 1 :(得分: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维护者会填充真正的原因:)

答案 2 :(得分:28)

  

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

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

也许你认为它会先进行过滤,然后将结果转储到某个形式的任意无序集合中 - 它不会。

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

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

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

请注意,虽然Collections2.filter不会急切地遍历基础集合,但Lists.newArrayList - 它将提取已过滤集合的所有元素并将其复制到新的{ {1}}。

答案 3 :(得分:12)

如Jon所述,您可以使用Iterables.filter(..)Collections2.filter(..),如果您不需要实时视图,则可以使用ImmutableList.copyOf(Iterables.filter(..))Lists.newArrayList( Iterables.filter(..))保持。

如果您对为什么部分感兴趣,可以访问https://github.com/google/guava/issues/505了解更多详情。

答案 4 :(得分:6)

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

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