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

@Myth: Я не бачу вашої точки зору ... Властивість length масивів не є загальнодоступним полем або що-небудь ще, це конструкція jvm (arraylength - це навіть операція в байт-коді). Це дуже очевидно, коли ви робите JNI або просто розбираєте код, звідки він походить.
Фредрік

Не думаю, що міркування оригінального дизайну полягали лише у виконанні. Як би ви реалізували, Vectorколи мова Java не підтримувала масив?
Holger

8

.lengthє одноразовою властивістю Java. Він використовується для пошуку розміру одновимірного масиву .

.length()є методом. Він використовується для знаходження довжини a 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.

Довжина рядка є випадковою; це не атрибут рядка, а побічний продукт. Хоча рядки Java насправді незмінні, якщо б можна було змінити їх вміст, ви могли б змінити їх довжину. Збивання останнього символу (якщо це було можливо) зменшило б довжину.

Я розумію, що це чудова відмінність, і я можу за це проголосувати, але це правда. Якщо я роблю масив довжиною 4, ця довжина чотирьох є визначальною характеристикою масиву і відповідає дійсності незалежно від того, що міститься в ньому. Якщо я роблю рядок, який містить "собаки", цей рядок має довжину 4, оскільки він, як видається, містить чотири символи.

Я бачу в цьому виправдання для того, щоб зробити одне з атрибутом, а інше - методом. По правді кажучи, це може бути просто ненавмисна непослідовність, але для мене це завжди мало сенс, і я завжди так про це думав.


2

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


10
Довжина масиву не може змінюватися протягом циклу.
Фредрік

2
ви не можете змінити довжину цього конкретного масиву, але ви можете призначити новий для тієї ж змінної
Божо

2
@Bozho: Правда, але це вже інший масив, і якщо ви це зробите навмисно і не оновлюєте те, що перевіряєте ваш цикл, у вас є помилка. Java ніколи не мала наміру заважати вам писати помилки. Ви можете робити нескінченні цикли, рекурсію до смерті та всілякі логічні помилки, не намагаючись зупинити вас від Java. Це є вагомою причиною для того, щоб не зберігати довжину в змінній, але це, безумовно, не причина, чому вона розроблена таким чином.
Фредрік

1

Щоразу, коли створюється масив, вказується його розмір. Тож довжину можна розглядати як атрибут конструкції. Для String це, по суті, масив char. Довжина є властивістю масиву char. Не потрібно ставити довжину як поле, оскільки це поле потрібно не всім. 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; д-р

На мій погляд, це вада дизайну Java, і її не слід було реалізовувати таким чином.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.