【问题标题】:Java - Best Strategy For Appending and Deleting In Giant Lists?Java - 在巨型列表中添加和删除的最佳策略?
【发布时间】:2013-04-10 00:44:24
【问题描述】:

使用 Java

我为一些计算等记录小对象,我只需要最后的 x 千个。所以我想将第一个释放给垃圾收集器。但是由于从 ArrayLists 中删除是昂贵的......

以下很重要(不能更改)

  • 没有数据库
  • 对象是同一类型
  • 每秒最多 50,000 个对象
  • 性能很重要
  • 快速迭代整个列表很重要
  • 随机访问也很重要

这可以改变:

  • 现在使用ArrayList<MyObject>
  • 限制:100,000 个对象(停止记录,但必须继续)

我的猜测:

  • 链表
  • 环形缓冲区
  • ???

如何快速迭代并同时快速释放旧对象?

【问题讨论】:

  • 如果您可以将数据(键-值)格式设置为更好的性能,则可以使用哈希图或树图。

标签: java performance list


【解决方案1】:

解决方案

... 如果您需要恒定数量的最后一个元素,只需使用数组作为环形缓冲区的基础。

  • 单次分配

  • 没有获取/放置/等。内联的方法开销

  • 简单

一个示例(可能无法编译,即时编写)实现:

class LastElementsStore<T> {
  Object[] arr;
  int size;
  int nextPutIndex;

  LastElementsStore(int size ) {
    arr = new Object[size];
    this.size = size;
  }

  void put(T elt) {
    arr[nextPutIndex] = elt;
    nextPutIndex++;
    if (nextPutIndex == size) {
      nextPutIndex = 0;
    }
  }

  // getters of your choice

}

如果没有足够的元素,将返回空值。

如果需要排序,则从 nextPutIndex 开始,一直读到最后,然后到 0 继续读。

您可以完全控制内存,不会像 LinkedList 那样进行额外的节点分配,也不会像 ArrayList 那样调整大小。

一旦达到限制,旧对象就会自动释放

您的要求

  • 没有数据库——完成,只是使用了一个数组

  • 对象是同一类型——简单模板

  • 每秒最多 50,000 个对象 -- 如果数组无法处理,Java 中没有任何东西可以处理

  • 性能很重要——如上所述,访问数组没有额外开销 快速迭代整个列表很重要——尽可能快地迭代

  • 随机访问也很重要——数据是有序的,nextPutIndex/之后的第一个非空元素是第一个可用的

【讨论】:

  • 由于整个项目都使用数组列表,我将做一些不同的事情:扩展 ArrayList 并覆盖方法以使其将项目存储在一个环中。然后出于性能原因,我可能会从扩展中删除 ArrayList 并确保它仍然像您建议的直接数组一样正常工作。
  • @mini-me 不要扩展ArrayList! - 扩展集合是一种不好的做法。创建一个内部 ArrayList 字段并将您的功能委托给它。
  • 你能给出理由吗?我觉得很难只是相信。
  • @mini-me 我现在不能用谷歌搜索:/ 扩展集合存在问题,在某些情况下会导致添加双元素;一般的问题是您不了解集合的内部结构,您可能会遇到意外的行为。
  • 找到 1 个专业人士和 1 个反对人士。 Pro:ensureCapacity 在 Linux 上存在错误,Contra:无法创建 T[] a = new T[100];(“错误:无法创建 T 的通用数组”)。
【解决方案2】:

在不了解您的任何其他要求的情况下,某种类型的链表似乎是合适的。只需跟踪第一个元素(列表中的“头部”)和最后一个元素(“尾部”)。每个链接的元素引用下一个。添加到尾部,从头部移除。

添加和删除会非常快(O(1)),迭代也会非常简单。

但是,如果您需要随机访问元素,那么这个解决方案的效果会很差。

编辑

由于您已添加需要随机访问,因此链接列表将不起作用。如果在任何给定时间的最大对象数是固定的,您可以使用 ArrayList(或基本 Java 数组)并将其视为环绕缓冲区。到达末尾后,从头开始替换对象,并跟踪表示列表逻辑开始的索引。 (如果您使用基本数组,您还需要跟踪缓冲区中当前有多少元素——相当于List.size())。

当您替换缓冲区开头的对象时,旧对象将被自动释放并进行垃圾回收(只要它们没有在其他地方引用)。

【讨论】:

  • 抱歉忘记了:随机访问也很重要。
【解决方案3】:

我会使用一个数组,并实现一个 RingBuffer。 我已经这样做了,但是使用了许多单元测试来确保它始终有效。 我没有找到适合我需要的环形缓冲区,也许你有更多的运气。

【讨论】:

    猜你喜欢
    • 2011-05-26
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    • 2017-07-07
    • 2010-10-13
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    相关资源
    最近更新 更多