没有复制开销的ArrayList?

时间:2009-09-05 18:58:00

标签: java algorithm arrays list

有没有人知道List实现具有常量时间get(int index)(即实现RandomAccess)但是当它增长为{{3时不必复制整个列表做什么?

我认为实施可能与其他列表有关,例如

public class ChunkedList<T> implements List<T>, RandomAccess {
  private LinkedList<ArrayList<T>> chunks;
  public T get(int index) {
    return findCorrectChunk(index).get(computeChunkIndex(index));
  }
}

7 个答案:

答案 0 :(得分:2)

如果有这样的结构,每个人都会使用它而不是数组。

然而,我认为在大学讲座中我已经被告知了一个更接近的结构。它具有恒定的访问时间,并且向任意位置添加/移除元素的时间大多为O(sqrt(N)),并且仅当N跨越整数值的平方时,才需要O(N)。摊销时间为O(sqrt(N))。这是想法。

此结构中的N个项目存储在一个连续数组中,该数组被划分为sqrt(N)个sqrt(N)个连续元素块(可能,最后一个块包含的元素较少)。每个块都是ring buffer,第一个元素的位置存储在一个单独的sqrt(N)数组中。要访问元素,您应该确定它所在的块(采用一个除法),并在环形缓冲区内进行适当的移位(总和和模数)。这是一个不变的访问时间。

要在第i个位置之前添加元素,请确定元素将结束的块k,然后在k中标记每个块中的所有最后元素。sqrt(N)-1范围。将前一个块中的标记元素移位到最后一个块中的空闲槽,该块将是那里的环形缓冲区的头部(访问附加阵列以确定确切位置)。然后,从前一个块移动到移动元素的位置,从前一个上一个块移动标记的元素。重复此操作,您将在数组中间获得一个空闲插槽,以放置您要添加的元素。

神奇之处在于,您应该只将附加数组中的值增加一(采用O(sqrt(N))时间),从而使结构一致再次访问。 sqrt(N)的魔力也在这里:你应该对每个X块和辅助阵列的每个N / X元素进行操作。达到(X + N / X)X = sqrt(N)。

如果最后一个块中没有地方添加一个元素(即到目前为止使用的sqrt(N)太小),则重新打包sqrt(N)增加1的数组。这需要O(N)时间。每个元素的摊销时间仍为O(sqrt(N))。

因此,在数组的任意位置添加元素需要O(sqrt(N))。删除需要相同的时间。访问时间为O(1)。

这就是主意。我不知道它是怎么称呼的,教授也不知道,因为他是自己发明的。任何参考将不胜感激。 OP可以实现它,但是,我敢打赌,有人已经拥有它。

答案 1 :(得分:1)

当然,您可以将列表实现编写为数组数组。关于确切的算法有很多选择。性能在理论上是恒定的(忽略缓存效应等)。

在实践中,大多数情况都没有太多意义。有绳索实现(字符串形成为一个段的数组),但这些是相对罕见的。该副本并不是那么昂贵,并且对于附加,它会在许多操作中摊销以便消失。

(顺便说一句,在问题示例代码中,LinkedList不合适,因为它几乎总是如此。)

答案 2 :(得分:0)

嗯,这不是一个理想的解决方案,但你可以使用TreeMap。你的ChunkedList将成为一个包装器。 TreeMap中的键将为Integer或Long类型,并保存列表索引。访问和插入时间将是o(log(n))(不是常数,但比n好得多)。内部TreeMap与LinkedList的工作方式类似,即节点只与引用链接。

编辑:类似的东西:

public class ChunkedList<T> implements List<T>, RandomAccess {

    private TreeMap<Integer, T> data = new TreeMap<Integer, T>();

    public T get(int index) {
        return data.get(index);
    }

    public boolean add(T o) {
        data.put(data.size() + 1, o);
        return true;
    }

       // Other operations

}

当然,其他操作会稍微复杂一些,并且比ArrayList需要更长的时间。

答案 3 :(得分:0)

你有没有看到暗示ArrayList构造函数的最大大小?

答案 4 :(得分:0)

查看随机访问列表。您可以在两端获得O(1)插入,并且可以对元素进行O(log(n))访问。

最后,某种树形结构应该提供最佳的查找/插入时间。

答案 5 :(得分:0)

这听起来像是过早优化。你有没有对add()函数进行分析并证明它很慢?因为ArrayList每次空间用完时都会使基础数组的大小加倍,所以每次追加时都不必复制列表。

您可能正在尝试解决不存在的问题。

答案 6 :(得分:0)

编写这样的数据结构是不可能的。您可以获得的最接近的是假设您知道最大值,将ArrayList预先调整为最大大小。有趣的是,如果标记ChunkedList,那么像{Collector.sort()这样的算法会在RandomAccess上执行更差