我相信LinkedLists和ArrayLists之间存在差异。 ArrayLists只是动态数组。所以我假设ArrayLists存储在连续位置的堆中(这就是他们有O(1)get方法的原因)。问题是,如果发生存在于堆中的另一个对象会阻止ArrayList增长,该怎么办?在这种情况下如何实施?如果ArrayList的剩余部分存储在堆的其他非concurous区域中,则get方法不会是O(1)。
例如,假设在内存位置10中有一个对象。之后,在内存位置5创建一个ArrayList。为简单起见,假设数组列表中的每个元素只是一个字节。这意味着ArrayList只能增长到5个元素。
答案 0 :(得分:4)
ArrayList
可以通过丢弃旧数组,分配一个全新的数组,并将旧数组中的值复制到新数组中,从而增长到可用内存限制的任何大小。对于任何给定的插入,此操作可以O(n)
amortized cost is O(1)
。
答案 1 :(得分:2)
所以我假设ArrayLists存储在堆中的连续位置
通常情况下,但没有什么能阻止JVM做出不同的事情(JVM规范中没有任何内容表明数组必须存储在连续的块中)。
如果碰巧存在堆中存在另一个会阻止ArrayList增长的对象会怎样?
arraylist对象将要求JVM为其提供更多空间,JVM将根据需要分配空间(如果不能,则抛出OutOfMemoryException)。
答案 2 :(得分:1)
查看源代码(感谢@GilbertoGaxiola),初始ArrayList创建一个大小为10的object []。因为它需要更多空间,所以它将大小翻倍并将旧数组的内容复制到新数组中。在这个阵列很大之前,它不需要很多次填充,这使得它成为一个很好的实现。
答案 3 :(得分:1)
所以我假设ArrayLists存储在连续位置的堆中(这就是他们有一个O(1)get方法的原因。)
所有其他答案都在这里是正确的,但我想解决你在这里做出的错误假设:
ArrayList.get()
是O(1),不是因为数组如何存储在内存中,而是因为元素访问的成本是恒定的,并且与中的元素数量不成比例 ArrayList。
O(1)这里并不意味着可以在单个操作中完成查找 - 这意味着相对于后备阵列的大小,复杂性是恒定的。
当您调用ArrayList.get(i)
时,JVM仍然需要将数组索引转换为虚拟内存地址,这需要您的操作系统将地址转换为真实地址 - 但它是O(1),因为复杂性是相同的如果数组有1个元素或5000个。
相比之下,LinkedList.get(i)
不是O(1),因为LinkedList类需要遍历每个节点,直到它找到节点i
- 复杂性与列表的整体大小成比例, i
的大小。
数组存储在连续位置的假设也是不正确的。
答案 4 :(得分:0)
最后的解释绝对正确。操作系统必须最终将索引转换为真实地址。因此,所有索引的翻译时间都是不变的。关于存储,最后一个解释是正确的,如果你需要存储更多元素,大小只会翻倍。