【发布时间】:2010-12-30 05:54:18
【问题描述】:
为什么我们有一个数组的长度作为属性array.length,而对于String我们有一个方法str.length()?
有什么原因吗?
【问题讨论】:
-
之前已经在 CodeRanch 讨论过。见讨论here.
为什么我们有一个数组的长度作为属性array.length,而对于String我们有一个方法str.length()?
有什么原因吗?
【问题讨论】:
稍微简化一下,您可以将其视为数组是一种特殊情况,而不是普通的类(有点像原语,但不是)。字符串和所有集合都是类,因此有获取大小、长度或类似内容的方法。
我猜当时设计的原因是性能。如果他们今天创建它,他们可能会想出类似数组支持的集合类之类的东西。
如果有人有兴趣,这里有一个小sn-p的代码来说明两者在生成代码中的区别,首先是源码:
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(或类似的东西)而不是一种行为并不像类但实际上是快速地。他们当然可以将属性作为方法调用进行掩码并在编译器中处理它,但我认为在不是真正类的东西上使用方法会更加混乱。
【讨论】:
Vector?
考虑:
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 等)做同样的事情,所以这很可能只是语言中的一个小缺陷。
如果你用错了,你会得到一个错误。
【讨论】:
我只是想通过Fredrik 为伟大的answer 补充一些意见。
Java Language Specification in Section 4.3.1 状态
一个对象是一个类实例或一个数组。
所以数组在Java中确实有着非常特殊的作用。我很想知道为什么。
有人可能会争辩说,当前的实现数组对于更好的性能很重要。但它是一个内部结构,不应该暴露。
他们当然可以将属性伪装成方法调用并在编译器中处理它,但我认为在不是真实类的东西上使用方法会更加混乱。
我同意 Fredrik 的观点,智能编译器优化会是更好的选择。这也可以解决问题,即使您使用数组的属性,您也没有解决字符串和其他(不可变)集合类型的问题,因为例如,string 是基于 char 数组,因为可以在String的类定义上看到:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; // ...
我不同意这会更令人困惑,因为数组确实继承了all methods from java.lang.Object。
作为一名工程师,我真的不喜欢“因为一直都是这样”的答案。并希望有更好的答案。但在这种情况下,它似乎是。
tl;dr
在我看来,这是Java的设计缺陷,不应该这样实现。
【讨论】:
.length 是 Java 的一次性属性。它用于查找一维数组的大小。
.length() 是一种方法。它用于查找 String 的长度。它避免了重复值。
【讨论】:
让我首先强调实现类似目的的三种不同方式。
length -- 数组 (int[], double[], String[]) -- 知道数组的长度
length() -- String相关对象 (String, StringBuilder, etc) -- 知道String的长度
size() -- Collection Object (ArrayList, Set, etc) -- 知道Collection的大小
现在忘记length(),只考虑length 和size()。
length 不是一种方法,因此它不适用于对象是完全有道理的。它只适用于数组。size() 它的名字更好地描述了它,因为它是一种方法,它将用于那些使用集合(集合框架)的对象,正如我在上面所说的那样。
现在就来length():
字符串不是原始数组(所以我们不能使用.length)也不是集合(所以我们不能使用.size())这就是为什么我们还需要一个不同的数组length()(保持差异并提供服务目的)。
回答为什么?
我觉得它很有用,易于记忆和使用,而且友好。
【讨论】:
每当创建数组时,都会指定其大小。所以长度可以被认为是一个构造属性。对于 String,它本质上是一个 char 数组。长度是 char 数组的一个属性。没有必要把长度作为一个字段,因为不是所有的东西都需要这个字段。 http://www.programcreek.com/2013/11/start-from-length-length-in-java/
【讨论】:
我被告知对于数组,由于以下恐惧,不会通过方法检索长度:程序员只会在进入循环之前将长度分配给局部变量(想想条件使用数组长度的 for 循环。 ) 程序员应该这样做是为了减少函数调用(从而提高性能)。问题是在循环期间长度可能会改变,而变量不会改变。
【讨论】:
在 Java 中,Array 将其长度与实际保存数据的结构分开存储。创建数组时,您指定其长度,这将成为数组的定义属性。无论你对一个长度为 N 的数组做什么(改变值、空出东西等),它始终是一个长度为 N 的数组。
字符串的长度是偶然的;它不是字符串的属性,而是副产品。尽管 Java 字符串实际上是不可变的,但如果可以更改它们的内容,则可以更改它们的长度。敲掉最后一个字符(如果可能的话)会缩短长度。
我知道这是一个很好的区别,我可能会因此而被否决,但这是真的。如果我制作一个长度为 4 的数组,则该长度为 4 是该数组的定义特征,并且无论其中包含什么都是正确的。如果我创建一个包含“dogs”的字符串,则该字符串的长度为 4,因为它恰好包含四个字符。
我认为这是用一个属性做一个,另一个用一个方法做的理由。事实上,这可能只是一种无意的不一致,但对我来说总是有意义的,而且我一直是这样想的。
【讨论】: