Java的ArrayList与JavaScript的Array是一个东西?

240 阅读4分钟

先说结论:

答案是no!

但是可以说java的ArrayList与JavaScript的数组在某些行为层面上是一样的,但是底层实现不同。

行为上

开发者使用中,Java的ArrayList与JavaScript的Array的长度都是可变的,这就给我们一种错觉,它俩是不是在底层实现上也是一样的呢,只不过语法不一样。请往下看

底层实现

Java

在Java中,ArrayList是基于数组实现的动态数组。它使用一个数组来存储元素,并提供了动态扩展和收缩的功能。当元素数量超出数组容量时,ArrayList会根据需要自动调整内部数组的大小,通常会创建一个更大的数组,并将原来数组中的元素复制到新数组中。这就是为什么向ArrayList添加或删除元素可能会导致数组重新分配的原因。

具体来说,当你创建一个ArrayList对象时,会分配一块连续的内存空间来存储元素。初始时,ArrayList的内部数组大小是根据初始容量来确定的,默认情况下初始容量为10。当元素数量超过数组的当前容量时,ArrayList会自动进行扩容,通常是以当前容量的一半进行扩展。

扩容过程中,ArrayList会创建一个更大的数组,并将原数组中的元素复制到新数组中,最后将原数组的内存释放掉。通过这种方式,ArrayList实现了动态调整大小的能力,可以根据需要增加或减少元素的数量。

由于ArrayList的底层实现是基于数组,所以可以通过索引直接访问数组中的元素,实现了高效的随机访问。但是,ArrayList在插入和删除元素时,需要对数组进行移动操作,可能会影响性能。

因此,在需要频繁地执行插入和删除操作的场景下,可能需要考虑使用LinkedList等其他数据结构来代替ArrayList。LinkedList底层使用链表实现,对于插入和删除操作更为高效。

JavaScript

在JavaScript中,数组是通过对象实现的。JavaScript中的数组可以存储不同类型的元素,并且长度是自动扩展的。当你向JavaScript数组添加新元素时,JavaScript引擎会自动分配内存来容纳新元素,并更新数组的长度。内部实现可能会使用类似动态数组或链表的数据结构。

JavaScript的Array在底层本质上就是特殊的对象,类似于

{
  0: 1,
  1: 2,
  2: {name: "张三"}对象的引用,
  length: 3
}

但是!在JavaScript中,虽然数组是一种特殊的对象,是以数字为索引并按顺序存储元素,但在底层实现上,并不直接使用散列表(哈希表)来存储元素。

在JavaScript中,数组的底层存储方式更接近于连续的内存空间,与哈希表相比较不同。底层实现通常使用称为"Elements"或"ElementsKind"的数据结构来存储数组的元素。

Elements的数据结构是一块连续的内存空间,用于存储数组的元素。在内存中,每个元素都被直接放在连续的位置上,这使得按索引访问数组元素非常高效。

在数组的初始化时,JavaScript引擎会根据需要为Elements分配一块初始大小的内存空间。当数组需要扩容时,引擎会创建一个新的更大的Elements,并将原有的元素复制到新的内存空间中。

与散列表不同,JavaScript中的数组在底层存储中并没有使用哈希码或散列函数来确定元素的存储位置。相反,它们使用数字索引来确定元素在内存中的偏移位置,从而实现高效的随机访问。

需要注意的是,尽管JavaScript的数组底层不是使用散列表存储,但对象的属性访问是基于散列表的。因此,对象和数组在底层存储方式上有所不同。

总结

虽然Java中的ArrayList和JavaScript中的数组都提供了动态长度的功能,但它们的底层实现是不同的。Java的ArrayList是使用固定大小的数组,并通过动态调整数组大小来实现的。而JavaScript的数组是基于对象的,能够动态增减长度。