2

如果我有一个ArrayList<Double> dblList和一个Predicate<Double> IS_EVEN,我可以从dblList使用中删除所有偶数元素:

Collections2.filter(dblList, IS_EVEN).clear()

然而,如果dblList是这样的转变的结果

dblList = Lists.transform(intList, TO_DOUBLE)

这不再起作用,因为转换后的列表是不可变的:-)

有什么解决办法吗?

4

6 回答 6

2

Lists.transform()接受一个 List 并有助于返回一个RandomAccess列表的结果。Iterables.transform()只接受一个I​​terable,结果不是RandomAccess。最后,Iterables.removeIf(据我所知,这是 Iterables 中唯一的一个)在给定参数是 RandomAccess 的情况下进行了优化,其目的是使算法线性而不是二次,例如想想什么如果你有一个大的 ArrayList(而不是 ArrayDeque - 这应该更流行)并且从它的开始一直删除元素直到它为空,就会发生这种情况。

但是优化不依赖于迭代器 remove(),而是依赖于List.set(),这在转换后的列表中是不可能支持的。如果要解决此问题,我们将需要另一个标记接口,以表示“可选的 set() 确实有效”。

所以你有的选择是:

  • 调用 Iterables.removeIf() 版本,并运行二次算法(如果你的列表很小或者你删除了几个元素都没有关系)
  • 将 List 复制到另一个支持所有可选操作的 List 中,然后调用 Iterables.removeIf()。
于 2010-07-08T08:46:04.553 回答
1

以下方法应该有效,尽管我还没有尝试过。

Collection<Double> dblCollection =
    Collections.checkedCollection(dblList, Double.class);
Collections2.filter(dblCollection, IS_EVEN).clear();

checkCollection() 方法生成未实现 List 的列表视图。[改为创建 ForwardingCollection 会更简洁,但更冗长。] 然后 Collections2.filter() 不会调用不受支持的 set() 方法。

库代码可以变得更加健壮。Iterables.removeIf() 可以生成一个组合的谓词,正如 Michael D 建议的那样,当传递一个转换后的列表时。但是,我们之前决定不通过添加这种特殊情况的逻辑来使代码复杂化。

于 2010-07-08T17:22:10.873 回答
0

也许:

Collection<Double> odds = Collections2.filter(dblList, Predicates.not(IS_EVEN));

或者

dblList = Lists.newArrayList(Lists.transform(intList, TO_DOUBLE));
Collections2.filter(dblList, IS_EVEN).clear();
于 2010-07-07T19:18:12.713 回答
0

经过一些尝试,我想我已经找到了:)

final ArrayList<Integer> ints = Lists.newArrayList(1, 2, 3, 4, 5);
Iterables.removeIf(Iterables.transform(ints, intoDouble()), even());
System.out.println(ints);

[1,3,5]
于 2010-07-08T08:14:43.407 回答
0

我没有解决方案,而是发现Iterables.removeIf()Lists.TransformingRandomAccessList.

转换后的列表实现了 RandomAccess,因此Iterables.removeIf()委托给Iterables.removeIfFromRandomAccessList()它依赖于不受支持的 List.set() 操作。但是调用Iterators.removeIf()会成功,因为 remove() 操作受Lists.TransformingRandomAccessList.

请参阅:可迭代:147

结论:instanceof RandomAccess 不保证 List.set()。

补充:在特殊情况下调用 removeIfFromRandomAccessList() 甚至可以工作:当且仅当要擦除的元素在列表尾部形成一个紧凑组或所有元素都被谓词覆盖。

于 2010-07-08T10:33:40.183 回答
0

只要您不需要中间集合,那么您就可以使用 Predicates.compose() 创建一个谓词,该谓词首先转换项目,然后对转换后的项目评估谓词。

例如,假设我有一个 List<Double> 我想从中删除整数部分为偶数的所有项目。我已经有一个 Function<Double,Integer> 给我 Integer 部分,还有一个 Predicate<Integer> 告诉我它是否是偶数。

我可以使用这些来获得一个新的谓词 INTEGER_PART_IS_EVEN

Predicate<Double> INTEGER_PART_IS_EVEN = Predicates.compose(IS_EVEN, DOUBLE_TO_INTEGER);
Collections2.filter(dblList, INTEGER_PART_IS_EVEN).clear();
于 2010-07-07T20:31:07.590 回答