在Java中增长数组的大多数内存有效方法?

我不太关心时间效率(操作很少),而是关于内存效率: 我可以在没有暂时拥有所有值两次的情况下增长数组吗?

是否有更有效的方法来扩展大型arrays而不是创建新arrays并复制所有值? 比如,将它与一个新的连接起来?

将固定大小的数组存储在另一个数组中并重新分配/复制那个顶级数组怎么样? 这会留下实际价值吗?

我知道ArrayList,但是我需要很多关于访问数组的控制,并且访问需要非常快。 例如,我认为我更喜欢a[i]al.get(i)

我关心这个问题的主要原因是所讨论的数组(或许多这样的数组)可能很好地占据主内存的足够大部分,在丢弃原始文件之前创建双倍大小的副本的通常策略可能不起作用出。 这可能意味着我需要重新考虑整体战略(或我的硬件建议)。

是否有更有效的方法来扩展大型arrays而不是创建新arrays并复制所有值? 比如,将它与一个新的连接起来?

没有。可能没有语言,这保证了arrays的发展总是在没有复制的情况下发生。 为数组分配空间并执行其他操作后,很可能在数组结束后的内存中有其他对象。 那时,根本不可能在不复制arrays的情况下扩展arrays。

将固定大小的数组存储在另一个数组中并重新分配/复制那个顶级数组怎么样? 这会留下实际价值吗?

你的意思是有一个数组数组,并把它当作一个由底层数组的串联组成的大数组? 是的,那会起作用(“伪造它做间接”的方法),就像在Java中一样, Object[][]只是一个指向Object[]实例的指针数组。

动态resize“数组”或项列表的最佳方法是使用ArrayList

Java已经在该数据结构中内置了非常有效的resize算法。

但是,如果必须调整自己的数组大小,最好使用System.arraycopy()Arrays.copyOf()

Arrays.copyOf()可以最简单地使用如下:

 int[] oldArr; int newArr = Arrays.copyOf(oldArr, oldArr.length * 2); 

这将为您提供一个与旧数组具有相同元素的新数组,但现在可以节省空间。

Arrays类通常有许多处理数组的好方法。

重要的是要确保每次添加元素时不仅要将数组增加一个元素。 最好实施一些策略,您只需每隔一段时间调整一次数组大小。 调整arrays大小是一项昂贵的操作。

数组是恒定大小的 ,所以没有办法增长它们。 您只能使用System.arrayCopy复制它们才能高效。


ArrayList完全符合您的需求。 除非你投入相当长的时间,否则它的优化程度要比我们任何人都好。 它在内部使用System.arrayCopy。


更重要的是,如果你有一些巨大的阶段 ,你需要列表增长/减少,而其他的不需要增长/减少,你可以在其中进行数千次读取或写入。 假设你有一个巨大的性能需求,你认为ArrayList在读/写时太慢了。 您仍然可以将ArrayList用于一个巨大的阶段,并将其转换为另一个的数组。 请注意,仅当您的应用程序阶段很大时,这才有效。

如何将链表与仅包含引用的数组相结合。

链接列表可以增长而无需分配新内存,该arrays将确保您可以轻松访问。 每次数组变小时,您只需删除整个数组并从链表再次构建它。

替代文字

“我可以在没有暂时拥有所有值两次的情况下增长arrays吗?”

即使你复制数组,你也只会拥有所有的值。 除非您对值调用clone(),否则它们将通过引用传递给新数组。

如果你已经在内存中拥有了你的值,那么复制到新数组时唯一的额外内存开销是分配新的Object []数组,它根本不需要太多内存,因为它只是一个指向值对象的指针列表。

看看System.arraycopy 。

AFAIK是增长或减少数组的唯一方法是进行System.arraycopy

  /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to removed. * @return the element that was removed from the list. * @throws IndexOutOfBoundsException if index out of range (index * < 0 || index >= length). */ public static  T[] removeArrayIndex(T[] src, int index) { Object[] tmp = src.clone(); int size = tmp.length; if ((index < 0) && (index >= size)) { throw new ArrayIndexOutOfBoundsException(index); } int numMoved = size - index - 1; if (numMoved > 0) { System.arraycopy(tmp, index + 1, tmp, index, numMoved); } tmp[--size] = null; // Let gc do its work return (T[]) Arrays.copyOf(tmp, size - 1); } /** * Inserts the element at the specified position in this list. * Shifts any subsequent elements to the rigth (adds one to their indices). * * @param index the index of the element to inserted. * @return the element that is inserted in the list. * @throws IndexOutOfBoundsException if index out of range (index * < 0 || index >= length). */ public static  T[] insertArrayIndex(T[] src, Object newData, int index) { Object[] tmp = null; if (src == null) { tmp = new Object[index+1]; } else { tmp = new Object[src.length+1]; int size = tmp.length; if ((index < 0) && (index >= size)) { throw new ArrayIndexOutOfBoundsException(index); } System.arraycopy(src, 0, tmp, 0, index); System.arraycopy(src, index, tmp, index+1, src.length-index); } tmp[index] = newData; return (T[]) Arrays.copyOf(tmp, tmp.length); } 

显然,如果你连接数组或将它们复制过来,这里的重要部分就不是; 更重要的是你的arrays增长战略。 不难看出,增长arrays的一种非常好的方法是在它变满时总是加倍。 这样,您将把添加元素的成本转为O(1),因为实际的生长阶段只会相对很少发生。

一种方法是使用数组节点的链表。 这有点复杂,但前提是:

您有一个链表,列表中的每个节点都引用一个数组。 这样,您的arrays可以在不复制的情况下成长。 为了增长,您只需要在最后添加其他节点。 因此,“昂贵的”增长操作仅在每个M操作中发生,其中M是每个节点的大小。 当然,这假设您总是追加到最后并且您不会删除。

在这种结构中插入和移除是相当复杂的,但如果你可以避免它们,那么这是完美的。

这种结构的唯一损失(忽略插入和删除)是与gets。 获得的时间会稍长; 访问正确的节点需要访问链表中的正确节点,然后在那里获取。 如果中间有很多访问,这可能会很慢,但是有加速链表的技巧。

你有没有看过GNU Trove的高效java集合? 他们的集合直接存储主要内容以更好地使用内存。

数组本身是大的,还是引用大型ReferenceTypes?

PrimitiveType数组与数十亿个元素以及包含数千个元素的数组之间存在差异,但它们引用大类实例。

 int[] largeArrayWithSmallElements = new int[1000000000000]; myClass[] smallArrayWithLargeElements = new myClass[10000]; 

编辑:

如果您使用ArrayList进行性能考虑,我可以向您保证,它将与Array索引一样或多或少地执行。

如果应用程序具有有限的内存资源,您可以尝试使用ArrayList的初始大小(其中一个构造函数)。

为了获得最佳的内存效率,您可以使用ArrayList of Arrays创建一个容器类。

就像是:

 class DynamicList { public long BufferSize; public long CurrentIndex; ArrayList al = new ArrayList(); public DynamicList(long bufferSize) { BufferSize = bufferSize; al.add(new long[BufferSize]); } public void add(long val) { long[] array; int arrayIndex = (int)(CurrentIndex / BufferSize); if (arrayIndex > al.size() - 1) { array = new long[BufferSize]; al.add(array); } else { array = (long[])al.get(arrayIndex); } array[CurrentIndex % BufferSize] = val; } public void removeLast() { CurrentIndex--; } public long get(long index) { long[] array; int arrayIndex = (int)(index / BufferSize); if (arrayIndex < al.size()) { array = (long[])al.get(arrayIndex); } else { // throw Exception } return array[index % BufferSize]; } } 

(我的java生锈了,所以请耐心等待......)

下面是添加和删除集合/ arraylist / vector中元素所花费时间的基准