tl;博士
操作对象的引用数组对对象没有影响,并且对对象在内存中的位置没有影响。
对象
对象的array 实际上是对象的引用(指针)数组。 pointer 是内存中另一个位置的地址。
我们将数组称为保存对象,但这在技术上并不准确。因为 Java 本身并不向我们作为程序员公开指针,所以我们通常不知道它们的存在。当我们访问数组中的一个元素时,我们实际上是在检索一个指针,但 Java 会立即跟随该指针来定位内存中其他位置的对象。
这种自动查找,跟随指向对象的指针,使指针数组感觉就像一个对象数组。 Java 程序员认为她的数组保存着她的对象,而实际上这些对象距离hop-skip-and-a-jump 远。
Java 中的数组被实现为连续的内存块。对于对象数组,指向这些对象的指针存储在连续内存中。但是当我们访问元素时,我们会跳转到内存中的另一个位置来访问我们想要的实际对象。
添加元素可能是“便宜的”,因为如果内存恰好在内存中的隔壁可用,则可以将其分配给数组以为更多元素腾出空间。在实践中,这不太可能。很有可能一个新数组必须在内存的其他地方构建,所有指针都被复制到新数组,然后丢弃原始数组。
这样的新数组和复制是“昂贵的”。在可行的情况下,我们希望避免这种操作。如果您知道数组可能的最大大小,请在声明数组时指定该大小。整个连续内存块被立即声明,数组中的内容为空,直到您稍后为元素分配一个指针。
插入数组的中间也很昂贵。要么构建一个新数组并复制元素,要么必须将插入点之后的所有元素向下移动到它们的相邻位置。
对数组的这些操作都不会影响对象。 对象在内存的以太中四处飘荡。对象对数组一无所知。对数组的操作不会影响对象,也不会影响它们在内存中的位置。唯一的关系是,如果数组中保存的引用是最后一个仍然指向该对象的引用,那么当该数组元素被清除或删除时,该对象将成为garbage-collection 的候选对象。
原语
在 Java 中,八种 primitive 类型(byte、short、int、long、float、double、boolean 和 char)不是对象/类而不是Object-Oriented Programming。与对象相比,它们的一个优点是速度快且占用内存少。
基元数组将值保存在数组本身中。因此,这些值彼此相邻存储,在内存中是连续的。没有参考/指针。不要在内存中乱跳。
至于添加或插入,上述行为同样适用。除了不是指针被打乱,实际的原始值被打乱。
提示
在商业应用中,通常最好使用对象。
这意味着使用包装类而不是原语。例如,Integer 而不是 int。 Java 中的auto-boxing 工具通过在原始值和它们的对象包装器之间自动转换,使这变得更容易。
首选对象意味着使用Collection 而不是数组,通常是List,特别是ArrayList。或者对于不可变的使用,从新的 List.of 方法返回一个 List 实现。
与商业应用相比,在速度和内存使用率至关重要的极端情况下(例如您的游戏引擎),则应充分利用数组和原语。
将来,如果Project Valhalla 中所做的工作取得成果,对象和原语之间的区别可能会变得模糊。