Java中的length和length()是什么?

91
为什么数组的长度是一个属性,即array.length,而对于字符串,我们则使用了一个方法str.length()
这样做有什么原因吗?

3
此前在CodeRanch已经讨论过此问题。请查看此处的讨论链接:http://www.coderanch.com/t/383558/Java-General/java/String-length-vs-Arrays-length - missingfaktor
8个回答

105

首先,让我强调三种相似的目的的不同方法。

length -- 数组 (int[], double[], String[]) -- 用于获取数组的长度

length() -- 字符串相关的对象 (String, StringBuilder, 等等) -- 用于获取字符串的长度

size() -- 集合对象 (ArrayList, Set, 等等) -- 用于获取集合的大小

现在忘掉length(),只考虑lengthsize()

length不是一个方法,因此它完全可以理解为它不能用于对象。它只适用于数组。
size()的名称描述得更好,因为它是一个方法,所以它将用于那些使用集合(集合框架)的对象,就像我之前提到的那样。

现在来看length()
String不是原始数组(因此无法使用.length),也不是集合(因此我们无法使用.size()),这就是为什么我们还需要另一个方法length()(保持差异并服务于目的)。

作为回答为什么?
我发现它有用,易于记忆和使用,而且友好。


6
太棒了的解决方案! - Jeff Hu

27
有点简化,你可以认为数组是一种特殊情况,而不是普通类(有点像原始类型,但又不完全相同)。String和所有集合都是类,因此有方法可以获取大小、长度或类似的东西。
我猜设计时的原因是性能。如果他们今天创建它,他们可能会想出一些基于数组的集合类。
如果有人感兴趣,这里是一个小代码片段来说明两者在生成的代码中的差异,首先是源代码:
public class LengthTest {
  public static void main(String[] args) {
    int[] array = {12,1,4};
    String string = "Hoo";
    System.out.println(array.length);
    System.out.println(string.length());
  }
}

削减字节码中不太重要的部分,运行javap -c命令对类进行反编译,最后两行输出如下:

20: getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: arraylength
25: invokevirtual   #4; //Method java/io/PrintStream.println:(I)V
28: getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_2
32: invokevirtual   #5; //Method java/lang/String.length:()I
35: invokevirtual   #4; //Method java/io/PrintStream.println:(I)V

在第一种情况下(20-25),代码仅向JVM请求数组的大小(在JNI中,这将是对GetArrayLength()的调用),而在字符串情况下(28-35),它需要进行方法调用以获取长度。

在上世纪90年代中期,没有好的JIT和其他东西,仅拥有java.util.Vector(或类似的东西)而不是一个行为非常快速的语言构造会完全影响性能。他们当然可以将属性掩盖为方法调用并在编译器中处理它,但我认为在不是真正的类的东西上具有方法会更加令人困惑。


1
Java将数组视为对象,字符串视为不可变对象!:| - Nitish Upreti
@Myth:我不明白你的观点...数组的长度属性不是公共字段或任何东西,它是jvm的构造(arraylength甚至是字节码中的操作)。当你做JNI或者只是反汇编代码时,它非常明显从哪里来。 - Fredrik
我认为原始设计的推理不仅仅是性能。如果Java语言没有数组支持,你会如何实现“Vector”? - Holger

8

考虑以下内容:

int[] myArray = new int[10];
String myString = "hello world!";
List<int> myList = new ArrayList<int>();

myArray.length    // Gives the length of the array
myString.length() // Gives the length of the string
myList.size()     // Gives the length of the list

很可能字符串和数组是在不同的时间设计的,因此它们最终采用了不同的约定。一个理由是,由于字符串在内部使用数组,因此使用方法length()避免了相同信息的重复。另一个理由是,使用方法length()有助于强调字符串的不可变性,尽管数组的大小也是不可改变的。
归根结底,这只是一个不一致性,如果语言从头重新设计,肯定会得到解决。据我所知,其他语言(如C#,Python,Scala等)没有做同样的事情,因此这很可能只是作为语言的一部分的轻微缺陷。
无论如何,如果您使用错误的内容,都会收到错误信息。

这是更具有说服力和公正的答案。 - Zubair Alam

7

.length是Java的一次性属性。它用于查找单维数组的大小。

.length()是一个方法。它用于查找String的长度。它避免了值的重复。


3
在Java中,数组将其长度与实际保存数据的结构分开存储。当创建一个数组时,您会指定它的长度,并且该长度成为数组的定义属性。无论对长度为N的数组进行何种操作(更改值、清空内容等),它始终是长度为N的数组。
字符串的长度是不相关的,它不是字符串的属性,而是副产品。尽管Java字符串实际上是不可变的,如果可能更改它们的内容,则可以更改它们的长度。如果可以删去最后一个字符,则会降低长度。
我理解这是一个微小的差别,我可能会因此被投票否决,但这是真的。如果我创建了一个长度为4的数组,则长度为4是该数组的一个定义特征,与其内部所包含的内容无关。如果我创建一个包含“dogs”的字符串,该字符串长度为4,因为它恰好包含四个字符。
我认为这就是使用属性和方法进行区分的理由。事实上,这可能只是一种无意识的不一致性,但这总是有意义的,也是我一直以来的思考方式。

2

我曾经学过,对于数组来说,不会通过方法获取其长度,因为有以下担忧:程序员会在进入循环之前将长度分配给一个局部变量(例如for循环中的条件使用了数组的长度)。程序员可能会这样做以减少函数调用(从而提高性能)。问题是,在循环期间长度可能会发生改变,但变量不会。


10
循环过程中数组的长度不能改变。 - Fredrik
2
你不能改变这个特定数组的长度,但是你可以将一个新的数组分配给同一变量。 - Bozho
2
@Bozho:没错,但这样又会生成另一个数组,如果你故意这么做,并且不更新你在循环中检查的内容,那就会出现bug。Java从来没有打算阻止你写bug。你可以创建无限循环、递归到死以及各种逻辑错误,而Java并不会试图阻止你。不将长度存储在变量中是有充分理由的,但这绝对不是设计的原因。 - Fredrik

1

0

我只是想在Fredrik的伟大答案中添加一些评论。

Java语言规范第4.3.1节指出:

对象类实例数组

因此,在Java中,数组确实具有非常特殊的作用。我不知道为什么。

有人可能会认为,当前实现的数组对于更好的性能至关重要。但那是一个内部结构,不应该被公开。

当然,他们本可以将属性掩盖为方法调用,并在编译器中处理它,但我认为在某些情况下,在不是真正的类上拥有方法会更加混乱。

我同意Fredrik的观点,即智能编译器优化是更好的选择。这也将解决问题,即使您对数组使用属性,也无法解决字符串和其他(不可变)集合类型的问题,因为例如`string`基于`char`数组,如您可以在`String`类定义中看到的:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {           
    private final char value[]; // ...

我不同意这会更加混乱的说法,因为数组确实继承了 java.lang.Object的所有方法。

作为一名工程师,我真的不喜欢答案是“因为一直都是这样”,希望有一个更好的答案。但在这种情况下,似乎就是这样。

简而言之:

在我看来,这是Java的设计缺陷,不应该以这种方式实现。


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