从另一个arraylist中删除一个arraylist元素的最佳方法

时间:2016-05-23 05:55:31

标签: java arraylist removeall

Java(7,8)中用于消除一个integer的{​​{1}}个元素的最佳性能方法是什么?所有元素在第一和第二个列表中都是唯一的。

目前我知道API方法Arraylist并以这种方式使用它:

removeall

当我使用arraylists进行操作时,会出现问题超过10000个元素。例如,当我删除65000个元素时,延迟似乎约为2秒。但我需要使用超过1000000个元素的更大的列表进行操作。

这个问题的策略是什么?

使用新的Stream API可能会解决它吗?

3 个答案:

答案 0 :(得分:15)

<强> TL; DR:

保持简单。使用

list.removeAll(new HashSet<T>(listOfElementsToRemove));

代替。

正如Eran在his answer中已经提到的那样:性能低下的原因是通用removeAll实现的伪代码

public boolean removeAll(Collection<?> c) {
    for (each element e of this) {
        if (c.contains(e)) {
            this.remove(e);
        }
    }
}

因此,对要删除的元素列表执行的contains调用将导致O(n * k)性能(其中n是要删除的元素数,{{1调用方法的列表中元素的数量)。

天真地,人们可以想象k上的this.remove(e)调用也可能有O(k),这种实现也会有二次复杂性。但情况并非如此:您提到列表是专门的List个实例。并且ArrayList方法被实现为委托给名为ArrayList#removeAll的方法,该方法直接在底层数组上运行,单独删除元素。

所以你要做的就是确保包含要删除的元素的集合中的查找很快 - 最好是O(1)。这可以通过将这些元素放入batchRemove来实现。最后,它可以写成

Set

附注:

Eran的回答有恕我直言的两个主要缺点:首先,它需要排序列表,即O(n * logn) - 并且它根本就没有必要。但更重要的是(显然):排序可能会改变元素的顺序!如果根本不需要这会怎么样?

远程相关:list.removeAll(new HashSet<T>(listOfElementsToRemove)); 实施中还涉及其他一些细微之处。例如,在某些情况下HashSet removeAll method is surprisingly slow。虽然当要删除的元素存储在列表中时,这也归结为O(n * n),但在这种特殊情况下,确切的行为可能确实令人惊讶。

答案 1 :(得分:10)

好吧,因为removeAll检查tempList的每个元素是否出现在tempList2中,所以运行时间与第一个列表的大小相乘,乘以第二个列表的大小list,表示O(N^2),除非两个列表中的一个非常小并且可以被视为“常量”。

另一方面,如果您对列表进行预排序,然后使用单次迭代迭代两个列表(类似于合并排序中的合并步骤),则排序将采用O(NlogN)和迭代O(N),总运行时间为O(NlogN)。这里N是两个列表中较大者的大小。

如果您可以按排序结构替换列表(可能是TreeSet,因为您说元素是唯一的),您可以在线性时间内实现removeAll,因为您不必做任何排序。

我还没有对它进行测试,但是这样的事情可以起作用(假设tempListtempList2都已排序):

Iterator<Integer> iter1 = tempList.iterator();
Iterator<Integer> iter2 = tempList2.iterator();
Integer current = null;
Integer current2 = null;
boolean advance = true;
while (iter1.hasNext() && iter2.hasNext()) {
    if (advance) {
        current = iter1.next();
        advance = false;
    }
    if (current2 == null || current > current2) {
        current2 = iter2.next();
    }
    if (current <= current2) {
        advance = true;
        if (current == current2)
            iter1.remove();
    }
}

答案 2 :(得分:2)

我怀疑从ArrayList中删除是一种性能命中,因为当删除中间的元素时,列表可能被分割,或者在删除元素后必须压缩列表。这样做可能会更快:

  1. 创建&#39;设置&#39;要删除的元素
  2. 创建一个你需要的新结果ArrayList,称之为R.你可以在构造时给它足够的大小。
  3. 通过原始列表迭代,您需要删除其中的元素,如果在Set中找到该元素,请不要将其添加到R,否则添加它。
  4. 这应该有O(N);如果创建Set并且其中的查找被假定为常量。