length и length () в Java

88

Почему у нас есть длина массива в качестве атрибута array.length, а для String у нас есть метод str.length()?

Есть ли причина?

Нитиш Упрети
источник
3
Обсуждалась ранее на CodeRanch. См. Обсуждение здесь.
missingfaktor

Ответы:

97

Позвольте мне сначала выделить три различных способа достижения той же цели.

length- массивы ( int[], double[], String[]) - знать длину массивов

length()- Объект, связанный со строкой ( String, StringBuilderи т.д.) - чтобы узнать длину строки

size()- Объект Коллекции ( ArrayList, Setи т. Д.) - чтобы узнать размер Коллекции

Теперь забудьте о length()всём lengthи size().

lengthне является методом, поэтому вполне понятно, что он не будет работать с объектами. Он работает только с массивами.
size()его название лучше описывает его, и, поскольку это метод, он будет использоваться в случае тех объектов, которые работают с коллекцией (фреймворки коллекций), как я сказал там.

Теперь переходим к следующему length():
String не является примитивным массивом (поэтому мы не можем использовать .length), а также не коллекцией (поэтому мы не можем использовать .size()), поэтому нам также нужен другой, который length()(сохраните различия и служите цели).

Как ответ на вопрос " Почему?"
Я считаю его полезным, легким для запоминания, использования и дружелюбным.

Саиф
источник
6
классное решение!
Джефф Ху
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 (arrayylength - это даже операция в байт-коде). Это очень очевидно, когда вы выполняете JNI или просто дизассемблируете код, из которого он пришел.
Фредрик
Я не думаю, что исходный дизайн исходил только из производительности. Как бы вы реализовали, Vectorесли в языке Java не было поддержки массивов?
Хольгер
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 и т. Д.) Не делают то же самое, так что это, вероятно, всего лишь небольшая ошибка, которая в итоге стала частью языка.

Вы все равно получите сообщение об ошибке, если воспользуетесь не тем.

Гордон Густафсон
источник
Это более справедливый и объективный ответ.
Зубаир Алам
3

В Java массив хранит свою длину отдельно от структуры, в которой фактически хранятся данные. Когда вы создаете массив, вы указываете его длину, и это становится определяющим атрибутом массива. Независимо от того, что вы делаете с массивом длины N (изменяете значения, выводите пустые данные и т. Д.), Он всегда будет массивом длины N.

Длина строки случайна; это не атрибут String, а побочный продукт. Хотя строки Java на самом деле неизменяемы, если бы можно было изменить их содержимое, вы могли бы изменить их длину. Сбивание последнего символа (если бы это было возможно) уменьшило бы длину.

Я понимаю, что это прекрасное различие, и меня могут проголосовать против него, но это правда. Если я сделаю массив длиной 4, эта длина, равная четырем, будет определяющей характеристикой массива и будет истинной независимо от того, что содержится внутри. Если я создаю строку, содержащую «собак», эта строка будет иметь длину 4, потому что она содержит четыре символа.

Я рассматриваю это как оправдание того, что делать одно с атрибутом, а другое с методом. По правде говоря, это может быть просто непреднамеренное несоответствие, но для меня это всегда имело смысл, и я всегда так думал об этом.

джейкбоксер
источник
2

Меня учили, что для массивов длина не извлекается с помощью метода из-за следующего опасения: программисты просто присваивают длину локальной переменной перед входом в цикл (представьте цикл for, в котором условное выражение использует длину массива). предположительно будет делать это, чтобы сократить количество вызовов функций (и тем самым повысить производительность). Проблема в том, что длина может измениться во время цикла, а переменная - нет.

Мэтью
источник
10
Длина массива не может изменяться во время цикла.
Фредрик
2
вы не можете изменить длину этого конкретного массива, но вы можете назначить новый массив той же переменной
Bozho
2
@Bozho: Верно, но тогда это еще один массив, и если вы делаете это намеренно и не обновляете то, что вы проверяете в своем цикле, у вас есть ошибка. У Java никогда не было намерения помешать вам писать ошибки. Вы можете делать бесконечные циклы, рекурсию до самой смерти и всевозможные логические ошибки, при этом java не пытается вам помешать. Это хорошая причина не хранить длину в переменной, но это точно не причина, по которой она так спроектирована.
Фредрик
1

Каждый раз при создании массива указывается его размер. Так что длину можно рассматривать как конструктивный атрибут. Для String это, по сути, массив символов. Длина - это свойство массива символов. Нет необходимости указывать длину как поле, потому что это поле требуется не всем. http://www.programcreek.com/2013/11/start-from-length-length-in-java/

Райан
источник
0

Я просто хочу , чтобы добавить некоторые замечания к великому ответу по Фредрика .

Спецификация языка Java в разделе 4.3.1 состояния

Объект является экземпляром класса или массив .

Таким образом, массив действительно играет особую роль в Java. Интересно, почему?

Можно утверждать, что текущий массив реализаций важен / был важен для лучшей производительности. Но это внутренняя структура, которую нельзя обнажать.

Конечно, они могли бы замаскировать свойство как вызов метода и обработать его в компиляторе, но я думаю, что было бы еще более запутанным иметь метод для чего-то, что не является настоящим классом.

Я согласен с Фредриком, что разумная оптимизация компилятора была бы лучшим выбором. Это также решит проблему, заключающуюся в том, что даже если вы используете свойство для массивов, вы не решили проблему для строк и других (неизменяемых) типов коллекций, потому что, например, они stringоснованы на charмассиве, как вы можете видеть в определении класса из String:

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

И я не согласен с тем, что это было бы еще более запутанным, потому что массив наследует все методы изjava.lang.Object .

Мне как инженеру очень не нравится ответ «Потому что так было всегда». и хотелось бы лучшего ответа. Но в данном случае вроде бы.

tl; dr

На мой взгляд, это недостаток дизайна Java, и его не следовало реализовывать таким образом.

Майкл Дорнер
источник