为什么java中的数组需要预定义长度,而对象不需要?

fsi0uk1n  于 2021-06-29  发布在  Java
关注(0)|答案(4)|浏览(375)

抱歉,如果这是一个非常愚蠢的问题,但是听到“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为例)
我希望这现在更有意义。。。我不知道为什么大家都误解了(我说错什么了吗?如果是,请告诉我,我会为他人的方便而更改)

xmd2e60i

xmd2e60i1#

为什么java中的数组需要预定义长度,而对象不需要?
为了理解这一点,我们需要确定“大小”在java中是一个复杂的概念。有多种含义:
每个对象作为一个或多个堆节点存储在堆中,其中一个是主节点,其余的是可以从主节点访问的组件对象。
主堆节点由固定和不变的堆内存字节数表示。我将把它称为对象的本机大小。
数组具有显式 length 现场。未声明此字段。它有一个类型 int 不能分配给。实际上,每个数组示例的头中都有一个32位字段,其中包含 length .
这个 length 数组的大小直接Map到其本机大小。jvm可以根据 length .
不是数组示例的对象也具有本机大小。这是由对象的fi的数量和类型决定的

a5g8bdjr

a5g8bdjr2#

我的问题是,为什么指向数组的指针需要事先知道数组有多大,而指向任何其他对象的指针却不知道?
没有。在这里,它运行得非常好:

String[] x = new String[10];
x = new String[15];

整个“需要提前知道它有多大”只指数组对象。就像在, 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 有收缩和生长的能力。
为什么数组不允许呢?因为不断缩小和增长的内容是复杂的,而且数组暴露了低级功能。不要使用数组,使用列表。

46qrfjad

46qrfjad3#

我将忽略你提供的大部分细节,并在编辑中回答问题。
我的问题是,为什么当我制作一个数组时,我需要告诉它它所指向的对象有多大,而当我制作任何其他对象时,它会自己缩放而不事先告诉它任何东西?
从“当我制作任何其他对象时,它会自行缩放”开始是值得的,因为这不是真的。如果创建这样的类:

class MyInteger
  public int value;
  public MyInteger(int value) {
    this.value = value;
  }
}

然后该类具有静态定义的大小。编译完这个类后 MyInteger 已经确定。在本例中,它是对象头大小(依赖于jvm)和整数大小(至少4个字节)。
一旦jvm分配了一个对象,它的大小就不能改变。jvm(更重要的是,垃圾收集器)将其视为一个字节块,直到它被回收。类,如 ArrayList 给人一种增长的错觉,但它们实际上是通过分配其他对象来工作的,它们存储对这些对象的引用。

class MyArrayList {
  public int[] values;
  public MyArrayList(int[] values) {
    this.values = values;
  }
}

在这种情况下 MyArrayList 示例将始终使用相同的内存量(对象头大小+引用大小),但引用的数组可能会更改。我们可以这样做:

MyArrayList list = new MyArrayList(new int[50]);

这会分配一块内存给 list ,以及 list.values . 如果我们那样做 ArrayList 有效地(内部):

list.values = new int[500];

然后分配给 list 仍然是相同的大小,但是我们已经分配了一个新的块,然后在中引用它 list.values . 我们的老朋友 int[50] 没有引用(所以它可以被垃圾收集)。不过,重要的是,分配规模没有改变。我们重新分配了一个新的、更大的块供我们的列表使用,并从我们的列表中引用了它 MyArrayList 示例。

yfjy0ee7

yfjy0ee74#

你似乎误解了“存储”的含义。你说“我可以存储的数据量没有限制”,但是如果你运行 myInteger.myValue = 15 ,则覆盖原来放在那里的值32。您仍然不能存储超过32位的数据,只不过您可以更改在该变量中输入的32位。
如果你想看看 ArrayList 作品,可以阅读源代码;它可以扩展,因为如果空间不足,它会创建一个新的更大的数组并切换其单个数组变量 elementData 去吧。
根据您的更新,您可能想知道是否有能力将许多不同的字段添加到对象定义中。在这种情况下,在编译类时,这些字段及其类型是固定的,从那时起,类的大小就固定了。不能像javascript那样在运行时堆入额外的属性。你是在事先告诉它需要的规模。

相关问题