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

Fab*_*ndl 86 java list filter guava

有没有理由呢

Lists.transform()
Run Code Online (Sandbox Code Playgroud)

但不是

Lists.filter()
Run Code Online (Sandbox Code Playgroud)

?

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

new ArrayList(Collection2.filter())
Run Code Online (Sandbox Code Playgroud)

当然,但是如果我理解正确,这种方式并不保证我的订单保持不变.

Dim*_*eou 57

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

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

  • 您假设将返回列表视图.但是#filter可以实现为返回一个新的实体化列表,这实际上是我对列表的过滤方法的期望,而不是Iterable上的过滤方法. (7认同)

Jon*_*eet 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);
Run Code Online (Sandbox Code Playgroud)

这将不得不迭代整个原始列表,将过滤器应用于所有内容.我想它可能要求谓词匹配在视图的生命周期内没有变化,但这并不完全令人满意.

(请注意,这只是猜测.也许其中一个Guava维护者会填充真正的原因:)

  • ...除非你稍后在代码中需要一个`view.size()`:) (3认同)

ska*_*man 28

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

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

也许你认为它在前面进行过滤,然后将结果转储到某种形式的任意,无序的集合中 - 它没有.

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

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

List filteredList = newArrayList(filter(originalList, predicate));
Run Code Online (Sandbox Code Playgroud)

请注意,虽然Collections2.filter不会急于在底层集合迭代,Lists.newArrayList 将 -它会提取过滤集合的所有元素,并将它们复制到一个新的ArrayList.


Pre*_*raj 12

正如Jon所说,您可以使用Iterables.filter(..)或者Collections2.filter(..)如果您不需要实时视图,则可以使用ImmutableList.copyOf(Iterables.filter(..))或者Lists.newArrayList( Iterables.filter(..))保持订购.

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


Hol*_*ndl 6

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

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