抱歉,如果这是一个非常愚蠢的问题,但是听到“java数组实际上只是对象”,那么它们需要预定义长度对我来说毫无意义?
例如,我理解原语类型为什么这样做 int myInt = 15;
分配32位内存来存储整数,这对我来说很有意义。但如果我有以下代码:
class Integer{
int myValue;
public Integer(int myValue){
this.myValue = myValue;
}
}
接着是一个 Integer myInteger = new Integer(15);myInteger.myValue = 5;
那么我可以存储的数据量就没有限制了 myInteger
. 它不限于32位,而是指向一个对象的指针,可以存储任意数量的数据 int
是的, double
是的, String
s、 或者什么的。它分配了32位内存来存储指针,但是对象本身可以存储任意数量的数据,并且不需要预先指定。
那么为什么数组不能这样做呢?为什么我需要事先告诉数组要分配多少内存?如果数组“实际上只是一个对象”,那么为什么我不能简单地说 String[] myStrings = new String[];myStrings[0] = "Something";
?
我对java非常陌生,所以有100%的可能这是一个愚蠢的问题,而且有一个非常简单明了的答案,但我很好奇。
另外,再举一个例子,我可以说 ArrayList<String> myStrings = new ArrayList<String>();myStrings.add("Something");
没有任何问题。。。那么是什么让arraylist不同于数组呢?为什么要告诉数组要分配多少内存,而arraylist却没有?
提前感谢任何花时间来填写我的人。:)
编辑:好吧,到目前为止评论中的每个人都误解了我的帖子,我觉得这是我的错。我的问题不是“如何定义数组?”,或“更改变量的值是否会改变其内存使用情况?”,或“指针是否存储它们指向的对象的数据?”,或“是数组对象?”,也不是“ArrayList如何工作?”我的问题是,当我创建一个数组时,为什么我需要告诉它它所指向的对象有多大,但是当我做任何其他物体时,它会自己缩放而不事先告诉它什么(以ArrayList为例)
我希望这现在更有意义。。。我不知道为什么大家都误解了(我说错什么了吗?如果是,请告诉我,我会为他人的方便而更改)
4条答案
按热度按时间xmd2e60i1#
为什么java中的数组需要预定义长度,而对象不需要?
为了理解这一点,我们需要确定“大小”在java中是一个复杂的概念。有多种含义:
每个对象作为一个或多个堆节点存储在堆中,其中一个是主节点,其余的是可以从主节点访问的组件对象。
主堆节点由固定和不变的堆内存字节数表示。我将把它称为对象的本机大小。
数组具有显式
length
现场。未声明此字段。它有一个类型int
不能分配给。实际上,每个数组示例的头中都有一个32位字段,其中包含length
.这个
length
数组的大小直接Map到其本机大小。jvm可以根据length
.不是数组示例的对象也具有本机大小。这是由对象的fi的数量和类型决定的
a5g8bdjr2#
我的问题是,为什么指向数组的指针需要事先知道数组有多大,而指向任何其他对象的指针却不知道?
没有。在这里,它运行得非常好:
整个“需要提前知道它有多大”只指数组对象。就像在,
new int[10]
去堆,这就像一个巨大的海滩,创造了一个新的宝箱出来的稀薄空气,大到足以容纳10个整数(这是原始的,就像在这个例子中的硬币)。然后把它埋在沙子里,永远消失。所以为什么new int[10];
所有的孤独都是无用的。当你写作的时候
int[] arr = new int[10];
,你仍然这样做,但你现在也做了一个藏宝图。x标记了这个点这是Map。它不是int数组。它是指向int数组的Map。在java中,两者[]
以及.
是“跟着Map,往下挖,然后打开急诊室”。arr[5] = 10;
意思是:跟着你的arr
Map,向下挖掘,打开你在那里找到的箱子,你会看到它有精确的10个小袋子的空间,每个袋子大到足以容纳一枚硬币。取第6袋。把里面的东西拿走,放一枚10吨的硬币进去。不是Map需要知道Map指向的胸部有多大。是箱子本身。对于对象也是如此,在java中不可能制作一个可以任意调整自身大小的宝箱。
那么arraylist是如何工作的呢?
盒子里的Map。
arraylist在内部有一个类型为的字段
Object[]
. 该字段不包含对象数组。不可能。它保存一个对象数组的Map:它是一个引用。那么,当你创建一个新的数组列表时会发生什么呢?这是一个固定大小的宝箱,正好可以放两样东西:
一张Map到一个“物体阵列”的宝箱(它也会做,有10张Map的空间,把它埋在沙子里,并把Map存储到这个宝箱里面。
硬币袋。里面的硬币表示列表中实际包含多少个对象。Map上的宝藏,它导致了宝藏的空间为10张Map,但这枚硬币(价值:0)说,迄今为止,没有这些Map去任何地方。
如果你跑了
list.add("foo")
,这是很复杂的:“foo”是一个对象(即宝藏),因此“foo”作为一个表达式解析为“foo”的Map。然后你就需要
list
宝藏Map,跟着它,往下挖,打开盒子,然后你大叫'哦!加上这个,把你的Map交给“福”财宝。这个盒子对你来说是不透明的,这就是oo的意义所在。但是让我们深入研究arraylist的来源:它将做的是,查询它的宝藏Map到对象数组(它是私有的,你无法访问它,它位于一个隐藏的隔间中,只有生活在宝箱中的djinn才能打开),跟随它,向下挖掘,然后转到第一个插槽(为什么?因为硬币袋中的“硬币大小”当前为0)。它把Map带到任何地方,把它扔出去,把你的Map复制到“foo”宝藏,然后把副本放在那里。然后,它将硬币袋中的硬币换成一便士,表示它现在的尺寸是1。
如果你加上第11个元素,arraylist djinn会转到另一个宝藏,注意到没有空间了,然后说:好吧,该死。可以。然后它变出一个全新的宝箱,可以容纳15张藏宝图,它复制了旧宝箱中的10张Map,将它们移到新的宝箱中,将你添加的东西的Map副本添加为第11张,然后回到自己的宝箱中,撕下Map到真正的宝藏,并取代它的Map,新制作的宝藏(15插槽),并把一个11克拉硬币在邮袋。
原来的宝箱就留在原处。如果没有人有这方面的Map(也没有人有),最终,海滩清洁工会找到它,把它扔掉(那就是垃圾收集器)。
因此,所有的宝箱都是固定大小的,但是通过用新Map替换Map和召唤新的宝箱,你仍然可以使它看起来像
ArrayList
有收缩和生长的能力。为什么数组不允许呢?因为不断缩小和增长的内容是复杂的,而且数组暴露了低级功能。不要使用数组,使用列表。
46qrfjad3#
我将忽略你提供的大部分细节,并在编辑中回答问题。
我的问题是,为什么当我制作一个数组时,我需要告诉它它所指向的对象有多大,而当我制作任何其他对象时,它会自己缩放而不事先告诉它任何东西?
从“当我制作任何其他对象时,它会自行缩放”开始是值得的,因为这不是真的。如果创建这样的类:
然后该类具有静态定义的大小。编译完这个类后
MyInteger
已经确定。在本例中,它是对象头大小(依赖于jvm)和整数大小(至少4个字节)。一旦jvm分配了一个对象,它的大小就不能改变。jvm(更重要的是,垃圾收集器)将其视为一个字节块,直到它被回收。类,如
ArrayList
给人一种增长的错觉,但它们实际上是通过分配其他对象来工作的,它们存储对这些对象的引用。在这种情况下
MyArrayList
示例将始终使用相同的内存量(对象头大小+引用大小),但引用的数组可能会更改。我们可以这样做:这会分配一块内存给
list
,以及list.values
. 如果我们那样做ArrayList
有效地(内部):然后分配给
list
仍然是相同的大小,但是我们已经分配了一个新的块,然后在中引用它list.values
. 我们的老朋友int[50]
没有引用(所以它可以被垃圾收集)。不过,重要的是,分配规模没有改变。我们重新分配了一个新的、更大的块供我们的列表使用,并从我们的列表中引用了它MyArrayList
示例。yfjy0ee74#
你似乎误解了“存储”的含义。你说“我可以存储的数据量没有限制”,但是如果你运行
myInteger.myValue = 15
,则覆盖原来放在那里的值32。您仍然不能存储超过32位的数据,只不过您可以更改在该变量中输入的32位。如果你想看看
ArrayList
作品,可以阅读源代码;它可以扩展,因为如果空间不足,它会创建一个新的更大的数组并切换其单个数组变量elementData
去吧。根据您的更新,您可能想知道是否有能力将许多不同的字段添加到对象定义中。在这种情况下,在编译类时,这些字段及其类型是固定的,从那时起,类的大小就固定了。不能像javascript那样在运行时堆入额外的属性。你是在事先告诉它需要的规模。