length i length () w Javie

88

Dlaczego mamy długość tablicy jako atrybut array.length, a dla String mamy metodę str.length()?

Czy jest jakiś powód?

Nitish Upreti
źródło
3
Zostało omówione wcześniej w CodeRanch. Zobacz dyskusję tutaj.
missingfaktor

Odpowiedzi:

97

Pozwólcie, że najpierw przedstawię trzy różne sposoby w podobnym celu.

length- tablice ( int[], double[], String[]) - znać długość tablic

length()- String związane Object ( String, StringBuilderitp) - znać długość łańcucha

size()- Collection Object ( ArrayList, Setitp) - znać wielkość Collection

Teraz zapomnij o length()rozważeniu tylko lengthi size().

lengthnie jest metodą, więc całkowicie ma sens, że nie będzie działać na obiektach. Działa tylko na tablicach.
size()jego nazwa lepiej to opisuje, a ponieważ jest to metoda, będzie używana w przypadku tych obiektów, które pracują z kolekcją (frameworkami kolekcji), jak powiedziałem tam wcześniej.

A teraz length():
String nie jest prostą tablicą (więc nie możemy jej używać .length), a także nie jest Kolekcją (więc nie możemy jej użyć .size()), dlatego potrzebujemy również innej, która jest length()(zachowaj różnice i służ do tego celu).

W odpowiedzi na pytanie Dlaczego?
Uważam, że jest użyteczny, łatwy do zapamiętania i użytkowania oraz przyjazny.

Saif
źródło
6
super rozwiązanie!
Jeff Hu,
27

Nieco upraszczając, możesz myśleć o tym jako o tablicach będących specjalnym przypadkiem, a nie o zwykłych klasach (trochę jak prymitywy, ale nie). String i wszystkie kolekcje są klasami, stąd metody uzyskiwania rozmiaru, długości lub podobnych rzeczy.

Myślę, że powodem w momencie projektowania była wydajność. Gdyby stworzyli go dzisiaj, prawdopodobnie wymyślili zamiast tego coś w rodzaju klas kolekcji opartych na tablicach.

Jeśli ktoś jest zainteresowany, oto mały fragment kodu ilustrujący różnicę między nimi w wygenerowanym kodzie, najpierw w źródle:

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());
  }
}

Odcięcie nie tak ważnej części kodu bajtowego, uruchomienie javap -cna klasie skutkuje następującym dla dwóch ostatnich wierszy:

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

W pierwszym przypadku (20-25) kod po prostu pyta JVM o rozmiar tablicy (w JNI byłoby to wywołanie GetArrayLength ()), podczas gdy w przypadku String (28-35) musi wykonać wywołanie metody, aby uzyskać długość.

W połowie lat 90., bez dobrych JIT-ów i innych rzeczy, całkowicie zabiłoby wydajność mając tylko java.util.Vector (lub coś podobnego), a nie konstrukcję językową, która tak naprawdę nie zachowuje się jak klasa, ale jest szybka. Mogliby oczywiście zamaskować właściwość jako wywołanie metody i obsłużyć ją w kompilatorze, ale myślę, że byłoby jeszcze bardziej zagmatwane, gdyby metoda była używana na czymś, co nie jest prawdziwą klasą.

Fredrik
źródło
1
Java traktuje tablicę jako obiekty, a łańcuchy jako niezmienne obiekty !! : |
Nitish Upreti
@Myth: Nie widzę twojego argumentu ... Właściwość length tablic nie jest polem publicznym ani czymkolwiek, jest konstrukcją jvm (długość tablicy to nawet operacja w kodzie bajtowym). Jest to bardzo oczywiste, gdy wykonujesz JNI lub po prostu demontujesz kod, skąd pochodzi.
Fredrik
Nie sądzę, aby rozumowanie pierwotnego projektu dotyczyło wyłącznie wydajności. Jak byś zaimplementował, Vectorgdy język Java nie miał obsługi tablic?
Holger,
8

.lengthjest jednorazową właściwością Javy. Służy do znalezienia rozmiaru jednowymiarowej tablicy .

.length()jest metodą. Służy do znajdowania długości String. Pozwala uniknąć powielania wartości.

Prabandha
źródło
8

Rozważać:

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

Jest bardzo prawdopodobne, że łańcuchy i tablice były projektowane w różnym czasie i dlatego korzystały z różnych konwencji. Jednym z uzasadnień jest to, że ponieważ Strings wewnętrznie używają tablic length(), użyto metody, aby uniknąć powielania tych samych informacji. Innym jest to, że użycie metody length()pomaga podkreślić niezmienność ciągów, mimo że rozmiar tablicy jest również niezmienny.

Ostatecznie jest to po prostu niespójność, która wyewoluowała, która z pewnością zostałaby naprawiona, gdyby język został kiedykolwiek przeprojektowany od podstaw. O ile wiem, żadne inne języki (C #, Python, Scala itp.) Nie robią tego samego, więc jest to prawdopodobnie tylko niewielka wada, która zakończyła się jako część języka.

Otrzymasz błąd, jeśli i tak użyjesz niewłaściwego.

Gordon Gustafson
źródło
To bardziej uzasadniona i bezstronna odpowiedź.
Zubair Alam
3

W Javie tablica przechowuje swoją długość oddzielnie od struktury, która faktycznie przechowuje dane. Tworząc Array, określasz jego długość, a to staje się definiującym atrybutem Array. Bez względu na to, co zrobisz z tablicą o długości N (zmień wartości, zerowe wartości itp.), Zawsze będzie to tablica o długości N.

Długość ciągu jest przypadkowa; nie jest atrybutem łańcucha, ale produktem ubocznym. Chociaż ciągi Java są w rzeczywistości niezmienne, gdyby można było zmienić ich zawartość, można by zmienić ich długość. Strącenie ostatniego znaku (jeśli byłoby to możliwe) zmniejszyłoby długość.

Rozumiem, że to dobre rozróżnienie i mogę zostać za to odrzucony, ale to prawda. Jeśli utworzę tablicę o długości 4, ta długość czterech jest cechą definiującą tablicę i jest prawdziwa niezależnie od tego, co jest w niej przechowywane. Jeśli utworzę ciąg zawierający „psy”, będzie on miał długość 4, ponieważ zdarza się, że zawiera cztery znaki.

Widzę to jako uzasadnienie zrobienia jednego z atrybutem, a drugiego z metodą. Prawdę mówiąc, może to być po prostu niezamierzona niespójność, ale zawsze ma to dla mnie sens i zawsze tak o tym myślałem.

jakeboxer
źródło
2

Nauczono mnie, że w przypadku tablic długość nie jest pobierana metodą z powodu następującego lęku: programiści przypisywaliby długość zmiennej lokalnej przed wejściem w pętlę (pomyśl o pętli for, w której warunek używa długości tablicy). podobno zrobiłby to, aby zmniejszyć liczbę wywołań funkcji (a tym samym poprawić wydajność). Problem polega na tym, że długość może się zmienić podczas pętli, a zmienna nie.

Mateusz
źródło
10
Długość tablicy nie może się zmienić podczas pętli.
Fredrik
2
nie możesz zmienić długości tej konkretnej tablicy, ale możesz przypisać nową do tej samej zmiennej
Bozho
2
@Bozho: Prawda, ale jest to inna tablica i jeśli zrobisz to celowo i nie aktualizujesz tego, co sprawdzisz w pętli, masz błąd. Java nigdy nie miała zamiaru powstrzymać Cię przed pisaniem błędów. Możesz robić nieskończone pętle, rekurencję aż do śmierci i wszelkiego rodzaju błędy logiczne bez javy próbującej cię przed tym powstrzymać. Jest to dobry powód, aby nie przechowywać długości w zmiennej, ale z pewnością nie jest to powód, dla którego jest tak zaprojektowana.
Fredrik
1

Za każdym razem, gdy tworzona jest tablica, określany jest jej rozmiar. Zatem długość można traktować jako atrybut konstrukcji. W przypadku String jest to zasadniczo tablica znaków. Długość jest właściwością tablicy char. Nie ma potrzeby podawania długości jako pola, ponieważ nie wszystko potrzebuje tego pola. http://www.programcreek.com/2013/11/start-from-length-length-in-java/

Ryan
źródło
0

Chcę tylko dodać kilka uwag do wielkiej odpowiedzi przez Fredrik .

Java Language Specification w pkt 4.3.1 państwa

Obiekt jest przykładem klasy lub matryca .

Tak więc tablica ma rzeczywiście bardzo szczególną rolę w Javie. Zastanawiam się, dlaczego.

Można argumentować, że obecna tablica implementacji jest / była ważna dla lepszej wydajności. Ale niż jest to struktura wewnętrzna, której nie należy eksponować.

Mogliby oczywiście zamaskować właściwość jako wywołanie metody i obsłużyć ją w kompilatorze, ale myślę, że byłoby jeszcze bardziej zagmatwane, gdyby metoda była używana na czymś, co nie jest prawdziwą klasą.

Zgadzam się z Fredrikiem, że mądra optymalizacja kompilatora byłaby lepszym wyborem. To również rozwiązałoby problem, że nawet jeśli używasz właściwości dla tablic, nie rozwiązałeś problemu z ciągami i innymi (niezmiennymi) typami kolekcji, ponieważ np. stringOpiera się na chartablicy, jak widać na definicji klasy z String:

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

I nie zgadzam się z tym, że byłoby to jeszcze bardziej zagmatwane, ponieważ array dziedziczy wszystkie metody zjava.lang.Object .

Jako inżynier bardzo nie podoba mi się odpowiedź „Bo tak było od zawsze”. i żałowałem, że nie ma lepszej odpowiedzi. Ale w tym przypadku tak się wydaje.

tl; dr

Moim zdaniem jest to wada projektowa Javy i nie powinna była być implementowana w ten sposób.

Michael Dorner
źródło