数组的length属性定义在哪里?

273

通过调用size()方法,我们可以确定一个ArrayList<E>的长度,例如:

ArrayList<Integer> arr = new ArrayList(10);
int size = arr.size();

同样地,我们可以使用length属性确定一个Array对象的长度。

String[] str = new String[10];
int size =  str.length;

ArrayListsize()方法定义在ArrayList类内部,而Arraylength属性定义在哪里?


就问题组织而言,我建议您将问题“Array的length属性在哪里定义?”放在所有解释之前,以避免您的帖子听起来像初学者教程。 - NoName
7个回答

261

在Java中,数组是特殊的对象,它们有一个简单的属性名为length,该属性是final的。

数组没有“类定义”(你在任何.class文件中都找不到它们),它们是语言本身的一部分。

10.7. 数组成员

数组类型的成员包括以下所有内容:

  • public final字段length,它包含数组的组件数量。length可以是正数或零。
  • public方法clone,它覆盖了类Object中同名的方法,并且不会抛出已检查异常。数组类型T[]clone方法的返回类型是T[]

    多维数组的克隆是浅层的,也就是说它仅创建一个新数组。子数组是共享的。

  • 从类Object继承的所有成员;Object的唯一未继承的方法是它的clone方法。

资源:


85
请注意,ArrayList.size() 返回的是实际存储在数组中的对象数量,而 myArray.length[])则提供了“容量”。也就是说,如果 myArray = new int[10];,它会返回 10。这并不是你放入数组中的对象数量。 - wmorrison365
1
@Colin 数组对象为什么如此特殊,为什么设计师需要将其制作成特殊的形式,而不是提供一个数组的类文件呢? - Vikas Verma
5
为什么数组不像对象呢?这是由于历史原因。在Java被设计时,大多数语言创新尝试都失败了,除非在语法和风格上类似于C。因此,在那个时代,C++、Objective-C和Java是少数几种摆脱了默默无闻的语言之一。Java有意识地设计为包括花括号、基本数据类型和普通数组,以便让当时的主流程序员感到熟悉。请参阅James Gosling和其他Sun公司人员的采访。一些人出于纯面向对象编程(OOP)的考虑,坚持使用集合并避免使用普通数组。 - Basil Bourque
1
@VikasVerma,在某个时候,Java必须建立在更低层次的技术上。如果没有Java,你就不能用Java来构建它。你必须使用C++(或其他东西,在这种情况下是C++)来构建一些原始编译,以便Java有上下文环境来存在。你不能拥有所有东西的类而不最终碰到底部,在那里你要用C++编写它。你将如何在Java中构建一个数组?如果你不能使用List,因为它们在实现中使用数组。 - Alexander Bird
如果你想按原样回答,可以访问https://dev59.com/vmox5IYBdhLWcg3wTija#50506451。 - Eugene

120

这个方法是“特殊”的,它有自己的字节码指令:arraylength。因此,该方法:

public static void main(String[] args) {
    int x = args.length;
}

代码会被编译成如下的字节码:

public static void main(java.lang.String[]);
  Code:
   0:   aload_0
   1:   arraylength
   2:   istore_1
   3:   return

因此,它不像普通字段那样被访问。实际上,如果您尝试像访问普通字段一样获取它,就像这样,它会失败:

// Fails...
Field field = args.getClass().getField("length");
System.out.println(field.get(args));

很遗憾,JLS对于每个数组类型都有一个公共的final字段length的描述有些误导人 :(


2
我不记得阿尔伯特·爱因斯坦的确切引言,但它表达的意思是“理解一件事情的人只能用非常简单的方式解释事情”,我认为它非常适合您 :) - JAVA
@Jon Skeet 数组对象有什么特殊之处,我的意思是为什么设计师需要将其特殊化,而不是提供一个数组的类文件? - Vikas Verma
2
@VikasVerma:考虑数组对象的大小。所有其他类型都有固定的大小-每个实例的大小相同,而数组的大小则根据其长度而变化。这只是一个例子。如果您认为可以在不使用任何特殊内容的情况下实现相同的结果,那么您认为数组类的字段会是什么样子?当泛型不适用于基本类型时,您将如何表示int[]?(而且,泛型在很长一段时间内都不存在。)您可以通过对所有集合使用链接列表来避免使用数组,但效率会非常低下。 - Jon Skeet
@VikasVerma:StringBuffer类本身确实会,因为它持有对char[]的引用(至少曾经如此;我不确定现在是否仍然是这样)。因此,虽然StringBuilder负责更多的内存,但其立即大小和布局是固定的。 - Jon Skeet
@JonSkeet 谢谢,现在数组的这种特殊行为对我来说有意义了。 - Vikas Verma
显示剩余5条评论

19

这个定义在Java语言规范中:

数组类型的成员包括以下内容:

  • public final字段length,它包含数组中组件的数量。长度可以是正数或零。

由于数组类型的数量是无限的(每个类都有相应的数组类型,还有多维数组),它们无法在类文件中实现;JVM必须动态创建它们。


15
尽管这不是对问题的直接回答,但它是关于.length.size()争论的一个补充。我正在研究与此相关的内容,所以当我遇到这个问题时,我注意到这里提供的定义

公共最终字段长度(数组组件数)。

并不是“完全”正确的。

字段长度包含可以放置组件的可用位置数量,而不是数组中存在的组件数量。因此,它表示为该数组分配的总可用内存,而不是该内存中有多少被填充了。

Array Memory Allocation

例如:

static class StuffClass {
    int stuff;
    StuffClass(int stuff) {
        this.stuff = stuff;
    }
}

public static void main(String[] args) {

    int[] test = new int[5];
    test[0] = 2;
    test[1] = 33;
    System.out.println("Length of int[]:\t" + test.length);

    String[] test2 = new String[5];
    test2[0] = "2";
    test2[1] = "33";    
    System.out.println("Length of String[]:\t" + test2.length);

    StuffClass[] test3 = new StuffClass[5];
    test3[0] = new StuffClass(2);
    test3[1] = new StuffClass(33);
    System.out.println("Length of StuffClass[]:\t" + test3.length);         
}

输出:

Length of int[]:        5
Length of String[]:     5
Length of StuffClass[]: 5

然而,ArrayList.size() 属性可以给出列表中元素的数量:
ArrayList<Integer> intsList = new ArrayList<Integer>();
System.out.println("List size:\t" + intsList.size());
intsList.add(2);
System.out.println("List size:\t" + intsList.size());
intsList.add(33);
System.out.println("List size:\t" + intsList.size());

输出:

List size:  0
List size:  1
List size:  2

2
这是正确的,因为所有元素都被初始化为零。数组不能是“空的”。 - Guido
那正是我所说的。它并不是“完全”正确的。即使没有向数组中添加任何内容,它仍然具有长度为5。因此,它并不“完全”包含数组中的元素数量。你的评论只是对我的答案的补充,而不是让它变得不正确的原因。 - nem035
如果你试图区分null和非null元素,那么这可能是“不正确”的。但是这种区分是行不通的。毕竟,例如Listsize()方法可能也包括null值。 - Stephen C
是的,我的观点基本上是size表现为动态,而length是静态属性... 数组中剩余未填充空间被初始化为“非值”(取决于类型)并不真正反驳这一点。 - nem035

5

这是一个公共的final字段,它包含数组的组件数(长度可以为正或零)。

因此,数组具有与以下类相同的公共字段和方法:

class A implements Cloneable, java.io.Serializable {
    public final int length = X;
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e.getMessage());
        }
    }
}

更多信息请参见

10.7 数组成员

http://java.sun.com/docs/books/jls/second_edition/html/arrays.doc.html


2
要直接回答问题,数组的 length 属性是在特殊的 Object header 中定义的。

通过 JOL 很容易看到。

 int [] ints = new int[23];
 System.out.println(ClassLayout.parseInstance(ints).toPrintable());

这个输出中的一行将会是:

其中涉及到的内容与IT技术有关。
OFFSET  SIZE      TYPE DESCRIPTION
16       4        (object header)   17 00 00 00 (00010111 00000000 00000000 00000000) (23)

通常对象有两个头部(标记和类),数组有一个额外的头部,它总是占用4字节的长度,因为size是一个int

-1
关键字长度就像一个定义的数据字段。在数组中使用时,我们可以使用它来访问数组中有多少个元素。对于String[],我们可以调用String类中定义的length()方法。对于ArrayList,我们可以使用ArrayList中定义的size()方法。请注意,当使用ArrayList<>(capacity)创建一个数组列表时,由于没有元素,此数组列表的初始size()为零。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接