Java中的length和length()


88

为什么我们将数组的长度作为属性,为什么将array.lengthString作为方法str.length()

有什么原因吗?


3
之前在CodeRanch已有讨论。请参阅此处
missingfaktor

Answers:


97

首先让我强调三种用于类似目的的不同方式。

length-阵列int[]double[]String[]) -知道数组的长度

length()-与字符串相关的对象StringStringBuilder等)-了解字符串的长度

size()- 集合对象ArrayListSet等)-知道集合的大小

现在忘记length()考虑公正lengthsize()

length不是方法,因此完全无法在对象上使用是完全有意义的。它仅适用于数组。
size()它的名称更好地描述了它,并且因为它是一种方法,将在我上面所说的那些使用集合(集合框架)的对象中使用。

现在来看length()
字符串不是原始数组(因此我们不能使用.length),也不是集合(因此我们不能使用.size()),这就是为什么我们还需要一个不同的数组length()(保持差异并达到目的)的原因。

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


6
很棒的解决方案!
Jeff Hu

27

稍微简化一下,您可以认为它是一种特殊情况,而不是普通类(有点像基元,但不是)。字符串和所有集合都是类,因此获取大小,长度或类似内容的方法。

我猜设计的原因是性能。如果他们今天创建了它,他们可能想出了一些类似数组支持的集合类。

如果有人感兴趣,下面是一小段代码,以说明所生成代码中两者之间的区别,首先是源代码:

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()的调用),而在String情况下(28-35),它需要执行以下操作:方法调用以获取长度。

在1990年代中期,如果没有良好的JIT和功能,仅使用java.util.Vector(或类似的东西),而不是像一个类却表现得并不快的语言构造,将完全丧失性能。他们当然可以将属性屏蔽为方法调用并在编译器中进行处理,但是我认为在不是真正类的东西上使用方法会更加令人困惑。


1
Java将数组视为对象,而将字符串视为不可变对象!:|
Nitish Upreti

@神话:我看不出你的意思……数组的length属性不是公共字段或其他任何东西,它是jvm的构造(arraylength甚至是字节码中的一个操作)。当您执行JNI或只是反汇编源代码时,这是非常明显的。
Fredrik

我不认为原始设计的理由仅仅是性能。Vector当Java语言不支持数组时,您将如何实现?
Holger

8

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

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


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

3

在Java中,数组的长度与实际保存数据的结构分开存储。创建数组时,请指定其长度,该长度成为数组的定义属性。无论您对长度为N的数组执行任何操作(更改值,清空内容等),它始终都是长度为N的数组。

字符串的长度是偶然的。它不是String的属性,而是副产品。尽管Java字符串实际上是不可变的,但如果可以更改其内容,则可以更改其长度。剔除最后一个字符(如果可能)将缩短长度。

我知道这是一个很好的区别,我可能会对此表示反对,但这是事实。如果我制作一个长度为4的数组,则该长度为4是该数组的定义特征,并且无论包含什么内容,它都是正确的。如果我创建一个包含“ dogs”的字符串,则该字符串的长度为4,因为它恰好包含四个字符。

我认为这是对一个属性进行处理而另一个对方法进行处理的理由。实际上,这可能只是无意间的不一致,但这对我来说总是有意义的,这就是我一直在思考的方式。


2

有人告诉我,对于数组,由于以下担心,无法通过方法检索长度:程序员在进入循环之前只会将长度分配给局部变量(想想for循环,其中条件语句使用数组的长度。)程序员据说这样做可以减少函数调用(从而提高性能)。问题是长度可能在循环期间改变,而变量不会改变。


10
循环期间数组的长度不能更改。
Fredrik

2
您无法更改此特定数组的长度,但可以将新变量分配给同一变量
Bozho

2
@Bozho:是的,但是那是另一个数组,如果您有意这样做并且不更新任何内容,请检查是否有错误。Java从未打算阻止您编写错误。您可以执行无限循环,递归直到死亡和各种逻辑错误,而无需Java试图阻止您退出。这是没有将长度存储在变量中的一个很好的理由,但可以肯定的是,它不是这样设计的。
Fredrik


0

我只是想一些言论增加了很大的答案弗雷德里克

第4.3.1节中Java语言规范指出

一个对象是一个类的实例阵列

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

有人可能会争辩说,当前的实现数组对于提高性能非常重要。但是比起它,它是一个内部结构,不应暴露出来。

他们当然可以将属性屏蔽为方法调用并在编译器中进行处理,但是我认为在不是真正类的东西上使用方法会更加令人困惑。

我同意Fredrik的观点,即明智的编译器优化将是更好的选择。这也将解决问题,即使将属性用于数组,也无法解决字符串和其他(不可变)集合类型的问题,因为例如,string基于char类的定义就可以看到数组的String

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {           
    private final char value[]; // ...

而且我不同意这样做会更加令人困惑,因为array确实继承了的所有方法java.lang.Object

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

tl; dr

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.