有没有理由
Lists.transform()
但没有
Lists.filter()
如何正确过滤列表?我可以用
new ArrayList(Collection2.filter())
当然,但是如果我理解的话,这种方式并不能保证我的订单保持不变。
答案 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));
}