【问题标题】:ArrayList remove vs removeAllArrayList 删除与 removeAll
【发布时间】:2015-05-01 20:34:06
【问题描述】:

如果我想从数组列表中删除一个集合,用什么更好? 我认为 ArrayList 中的 removeAll 方法是为此任务编写的,但在我编写的测试中,仅迭代对象并单独删除它们会快几秒钟。

您为此目的使用什么?

编辑:

我在 grepcode 上找到的 removeAll 代码调用 batchRemove (c, false):

private boolean More ...batchRemove(Collection c, boolean complement) {

700         final Object[] elementData = this.elementData;
701         int r = 0, w = 0;
702         boolean modified = false;
703         try {
704             for (; r < size; r++)
705                 if (c.contains(elementData[r]) == complement)
706                     elementData[w++] = elementData[r];
707         } finally {
708             // Preserve behavioral compatibility with AbstractCollection,
709             // even if c.contains() throws.
710             if (r != size) {
711                 System.arraycopy(elementData, r,
712                                  elementData, w,
713                                  size - r);
714                 w += size - r;
715             }
716             if (w != size) {
717                 // clear to let GC do its work
718                 for (int i = w; i < size; i++)
719                     elementData[i] = null;
720                 modCount += size - w;
721                 size = w;
722                 modified = true;
723             }
724         }
725         return modified;
726     }

其实我没看懂。。

我的测试代码是这样的:

public class RemoveVsRemovall {

    public static void main(String[] args){
        ArrayList<String> source = new ArrayList<>();
        ArrayList<String> toRemove = new ArrayList<>();
        for(int i = 0; i < 30000; i++){
            String s = String.valueOf(System.nanoTime());
            source.add(s);
            if(i % 2 == 0) toRemove.add(s);
        }
        long startTime = System.nanoTime();
        removeList1(source, toRemove);
        long endTime = System.nanoTime();
        System.out.println("diff: " + (endTime - startTime) * 1e-9);
    }

    static void removeList1(ArrayList<String> source, ArrayList<String> toRemove){
        source.removeAll(toRemove);
    }

    static void removeList2(ArrayList<String> source, ArrayList<String> toRemove){
        for(String s : toRemove){
            source.remove(s);
        }
    }
}

用不同的列表大小调用它几次并在两种方法之间切换。

【问题讨论】:

  • 我预计您的测试中存在缺陷。向我们展示您的测试代码。 (我发现很难相信在性能上真的存在显着差异。在 Java 中编写给出准确结果的基准相当困难。)
  • 您为什么不查看 remove 和 removeAll 方法的代码?然而,这个问题不值得被否决。 +1 来自我。有 200 多个赞成票的 SO 问题比这个更糟糕..
  • @bot,请问哪里有进展?
  • @Gabe 我不确定你的意思。
  • 正如我所怀疑的,基准代码存在缺陷。您没有正确“预热”JVM,这可能对removeAll 的影响比remove 的影响更大。

标签: java performance arraylist collections removeall


【解决方案1】:

有几个原因很难对这个问题给出一般性的答案。

首先,您必须了解这些性能特征取决于实现。很可能实现会因 JDK 的平台和版本而异。

话虽如此,实现removeAll的策略主要有两种:

  1. 对于ArrayList 的每个元素,检查它是否在另一个Collection 中;如果是这样,请将其删除。
  2. 对于Collection的每个元素,检查是否在ArrayList中;如果是这样,请将其删除。

如果Collection 在恒定时间内执行包含,则策略 1(渐近地)获胜。另一方面,如果contains是通过扫描整个连接来执行的,而Collection的迭代速度很慢,那么策略2通常具有优势,因为它只对Collection进行了一次迭代;但即使在这种情况下,如果Collection 非常大,并且ArrayList 的大部分元素都在Collection 的第一个元素中,那么策略1 再次获胜......没有尽头。

您最好相信removeAll() 的实现;如果失败,请尝试更改数据结构;如果这也失败了,请根据经验基准实施您自己的方法。

【讨论】:

    【解决方案2】:

    要考虑的另一件事:

    Java 的代码经过了多年的实战考验,其编写目的是为了适应许多不同的特殊情况(见评论Preserve behavioral compatibility with AbstractCollection)。

    因此,实际上您很可能可以编写自己的方法实现,这样运行速度会更快。但另一方面,你确定你能处理 Java 开发者自 Java 诞生以来所面临的所有特殊情况吗?

    还要考虑到一些 Java 函数可能会使用一些 C 实现来加快速度。这显然不是这里的情况,但它可以。

    【讨论】:

    • 所以你推荐使用 removeAll ?
    • 除非你真的关心最佳性能,并且你知道你只需要处理特定的数据集,否则它的行为类似于你的基准代码(我什至不确定是否可以轻松检查),是的。
    猜你喜欢
    • 2014-09-28
    • 2016-06-27
    • 2016-03-06
    • 1970-01-01
    • 2021-12-16
    • 2016-03-16
    • 2016-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多