【问题标题】:Faster Access Version of ArrayList?ArrayList 的更快访问版本?
【发布时间】:2017-09-01 07:36:28
【问题描述】:

有谁知道类似于 ArrayList 的东西,它更适合尽快处理大量数据?

我有一个带有非常大的 ArrayList 的程序,当它试图探索或修改 ArrayList 时,它会变得阻塞。

大概当你这样做时:

//i is an int;
arrayList.remove(i);

幕后的代码运行如下:

public T remove(int i){
    //Let's say ArrayList stores it's data in a T [] array called "contents".
    T output = contents[i];
    T [] overwrite = new T [contents.length - 1];
    //Yes, I know generic arrays aren't created this simply. Bear with me here...
    for(int x=0;x<i;x++){
        overwrite[x] = contents[x];
    }
    for(int x=i+1;x<contents.length;x++){
        overwrite[x-1] = contents[x];
    }
    contents = overwrite;
    return output;
}

当 ArrayList 的大小为几百万个单位左右时,所有这些重新排列数组中项目位置的循环将花费大量时间。

我试图通过创建我自己的自定义 ArrayList 子类来缓解这个问题,该子类将它的数据存储分割成更小的 ArrayList。任何需要 ArrayList 扫描特定项目的数据的进程都会为其中的每个较小的 ArrayList 生成一个新的搜索线程(以利用我的多个 CPU 内核)。

但是这个系统不起作用,因为当调用搜索的线程在任何 ArrayList 中同步了一个项目时,它可以阻止那些单独的搜索线程完成它们的搜索,这反过来又锁定了调用在搜索过程中,本质上使整个程序死锁。

我真的需要某种数据存储类,以在 PC 的能力范围内尽可能快地包含和操作大量对象。

有什么想法吗?

【问题讨论】:

  • 尝试使用 LinkedList
  • 数组怎么样?当然,它需要一些辅助函数。
  • 我会使用 ConcurrentLinkedHashMap
  • 我倾向于以 DUP 身份关闭 stackoverflow.com/questions/559839/… ...但现在:请检查该页面是否为您提供了开始所需的信息。
  • 与您的假设相反,ArrayList.remove 实现通常只是从i+1..endi..end-1 执行 System.arrayCopy。他们不分配任何额外的空间。 (请记住,ArrayList 的支持数组通常大于 ArrayList 的大小。) System.arrayCopy 通常是本机代码并且速度非常快。您确定是 remove 方法占用了 CPU 时间吗?

标签: java multithreading arraylist large-data-volumes


【解决方案1】:

链表的一个很好的使用例子是列表元素非常大,即。足够大,以至于只有一两个可以同时放入 CPU 缓存中。在这一点上,用于迭代的向量或数组等连续块容器的优势或多或少被抵消了,如果实时发生许多插入和删除,则可能会获得性能优势。

参考:Under what circumstances are linked lists useful?

参考:https://coderanch.com/t/508171/java/Collection-datastructure-large-data

【讨论】:

  • 在 Java 中,没有具有 large 元素的数组或 ArrayList。可能的最大元素是长指针或 64 位指针/引用。
  • 我关心的是列表中没有元素,而不是每个元素的大小
  • 嗯,我正在使用的元素主要是自定义对象,最终包含大约 60 个浮点数,可能还有十几个整数,给或取。大多数这些数字实际上都包含在其他自定义对象中。
【解决方案2】:

不同的集合类型对于不同的操作有不同的时间复杂度。典型的复杂性是:O(1)、O(N) 和 O(log(N))。要选择一个集合,您首先需要确定您经常使用的操作,并避免该操作具有 O(N) 复杂度的集合。在这里,您经常使用操作ArrayList.remove(i),即 O(N)。更糟糕的是,您使用remove(i) 而不是remove(element)。如果remove(element) 是唯一经常使用的操作,那么LinkedList 会有所帮助,它的remove(element) 是O(1),但LinkedList.remove(i) 也是O(N)。

我怀疑 Listremove(i) O(1) 的复杂性是否可以实现。最好的时间是 O(log(N)),这绝对比 O(N) 好。 Java 标准库没有这样的实现。您可以尝试通过“二进制索引树”关键字搜索它。

但我要做的第一件事是检查算法并尝试摆脱 List.remove(i) 操作。

【讨论】:

  • 那么我在使用ArrayList.remove(ArrayList.get(i)) 而不是仅仅使用ArrayList.remove(i) 时会有更好的性能吗?
  • @Cambot 当然不是。 ArrayList.remove(object)ArrayList.remove(i) 一样慢 - O(N),实际上甚至更慢。
  • 是的,我认为这可能不会让事情变得更好,因为如果确实如此,Java 人员肯定会重新编码 ArrayList 以使其以这种方式工作。
【解决方案3】:

我真的需要某种数据存储类,以在 PC 的能力范围内尽可能快地包含和操作大量对象。

答案很大程度上取决于您所谈论的数据类型以及您需要的具体操作。您使用工作“探索”而不定义它。

如果您正在谈论查找记录,那么对于线程操作,没有什么比 HashMapConcurrentHashMap 更好的了。如果您正在谈论保持秩序,尤其是在处理线程时,那么我建议您使用具有 O(logN) 查找、插入、删除等功能的 ConcurrentSkipListMap

您可能还想考虑使用多个集合。您需要注意不要让集合不同步,这对于线程来说尤其具有挑战性,但根据您正在执行的各种操作,这可能会更快。

当 ArrayList 的大小为几百万个单位左右时,所有这些重新排列数组中项目位置的循环将花费大量时间。

如前所述,ConcurrentSkipListMap 是 O(logN) 用于重新排列项目。即删除并添加新位置。

幕后的 [ArrayList.remove(i)] 代码运行如下:...

其实不是。你可以看看code in the JDK吧? ArrayList 使用 System.arraycopy(...) 进行此类操作。它们可能对您的情况无效,但不是O(N)

【讨论】:

  • 顺序很重要,所以听起来我需要ConcurrantSkipListMap。既然如此,我将如何使用此类复制操作ArrayList.add(int, Object)?这是我的程序使用的 ArrayList 的一个方面,所以任何替代品都需要能够执行类似的功能。
  • 嗯,您当然可以使用int 作为对象@Cambot 的键。所以你会 delete(int) 然后 put(int, object) 使用一个新对象。您还可以将int 作为字段添加到您的对象。如果您不控制该对象,那么您可以创建一个包装器对象来处理 hashCode()equals() 方法来为您执行此操作。
猜你喜欢
  • 1970-01-01
  • 2011-10-18
  • 2015-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多