番石榴:为什么没有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)