【问题标题】:Best way to remove one arraylist elements from another arraylist从另一个数组列表中删除一个数组列表元素的最佳方法
【发布时间】:2016-09-19 21:22:15
【问题描述】:

在 Java (7,8) 中,从另一个 Arraylist 中消除 integer 元素的最佳性能方法是什么。所有元素在第一个和第二个列表中都是唯一的。

目前我知道API方法removeall并这样使用:

tempList.removeAll(tempList2);

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

这个问题的策略是什么?

也许新的 Stream API 应该可以解决它?

【问题讨论】:

  • 将 tempList2 设为 HashSet,您可能会看到性能显着提升。
  • 您是否考虑过先对两个列表进行排序,然后简单地遍历第一个列表(您要从中删除项目的那个)?编辑:基本上是@Eran 在下面提出的。

标签: java arraylist removeall


【解决方案1】:

好吧,既然removeAll会检查tempList的每个元素是否出现在tempList2中,所以运行时间与第一个列表的大小乘以第二个列表的大小成正比,即@987654324 @ 除非两个列表之一非常小,可以认为是“恒定大小”。

另一方面,如果您预先对列表进行排序,然后通过一次迭代对两个列表进行迭代(类似于合并排序中的合并步骤),则排序将采用 O(NlogN) 和迭代 @987654326 @,总运行时间为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();
    }
}

【讨论】:

  • Eran,谢谢您的回复。你能分享一个你看到的代码sn-p吗? (单次迭代)
【解决方案2】:

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

  1. 创建要删除的元素的“集合”
  2. 创建一个您需要的新结果 ArrayList,将其命名为 R。您可以在构造时给它足够的大小。
  3. 遍历原始列表,您需要从中删除元素,如果在 Set 中找到该元素,则不要将其添加到 R,否则添加它。

这应该有O(N);如果创建 Set 并在其中查找,则假定为常量。

【讨论】:

    【解决方案3】:

    tl;博士:

    保持简单。使用

    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 是要删除的元素数,k 是要删除的元素数调用该方法的列表中的元素)。

    天真地,可以想象对Listthis.remove(e) 调用也可能有O(k),并且这种实现也将具有二次复杂度。但事实并非如此:您提到列表专门是 ArrayList 实例。并且 ArrayList#removeAll 方法被实现为委托给一个名为 batchRemove 的方法,该方法直接对底层数组进行操作,并且单独删除元素。

    因此,您所要做的就是确保在包含要删除的元素的集合中查找速度很快——最好是 O(1)。这可以通过将这些元素放入Set 来实现。最后可以写成

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

    旁注:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-01
      • 2023-03-23
      • 2022-11-13
      • 1970-01-01
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      相关资源
      最近更新 更多