Dlaczego w Javie nie ma String.Empty?


260

Rozumiem, że za każdym razem, gdy wpisuję literał ciąg "", w puli ciągów odwołuje się ten sam obiekt String.

Ale dlaczego String API nie zawiera public static final String Empty = "";, więc mógłbym używać odniesień do String.Empty?

Zaoszczędziłoby to przynajmniej czas kompilacji, ponieważ kompilator wiedziałby, że odwołuje się do istniejącego ciągu, i nie musiałby sprawdzać, czy został już utworzony do ponownego użycia, prawda? I osobiście uważam, że mnożenie literałów sznurkowych, szczególnie tych małych, w wielu przypadkach jest „zapachem kodu”.

Czy zatem nie istniał żaden wielki powód projektowy. Pusty, czy też twórcy języków po prostu nie podzielali się moimi poglądami?


5
Aidanc: Myślę, że oznacza on sytuacje, w których robisz takie rzeczy outputBlah = "", a on pewnie woli something == String.Emptynad something.Length > 0jak również (pominąć czek NULL.)
Skurmedel

2
@Aidanc - szukał „pustego elementu”, takiego jak Collections.EMPTY_SET , a nie funkcji sprawdzającej ciąg „pustki”.
Tim Stone,

2
@Aidanc: To, co zainspirowało to w rzeczywistości „TextBox.setText („ ”);”.
Tom Tresansky,

3
Jest String.isEmpty()funkcja ... dlaczego chcesz String.EMPTY?
Buhake Sindi,

8
String.isEmpty()nie zwraca pustego ciągu.
Steve Kuo,

Odpowiedzi:


191

String.EMPTYma 12 znaków i ""dwa, i oba odnoszą się dokładnie do tego samego wystąpienia w pamięci w czasie wykonywania. Nie jestem do końca pewien, dlaczego String.EMPTYmiałby oszczędzać na czasie kompilacji, w rzeczywistości myślę, że byłby to drugi.

Zwłaszcza biorąc pod uwagę, że Strings są niezmienne, nie jest tak, że możesz najpierw pobrać pusty Ciąg i wykonać na nim kilka operacji - najlepiej użyć StringBuilder(lub StringBufferjeśli chcesz być bezpieczny dla wątków) i przekształcić go w Ciąg.

Aktualizacja
Od komentarza do pytania:

Co właściwie to zainspirowało TextBox.setText("");

Uważam, że zapewnienie stałej w odpowiedniej klasie byłoby całkowicie uzasadnione:

private static final String EMPTY_STRING = "";

A następnie odwołaj się do niego jak w kodzie jako

TextBox.setText(EMPTY_STRING);

Ponieważ w ten sposób przynajmniej wyraźne jest, że chcesz pusty Łańcuch, a nie zapomniałeś wpisać Łańcuch w IDE lub coś podobnego.


14
Nadal będę Cię dawać +1, ale czuję się brudny, ponieważ wspomniałeś StringBuilderbez mówienia o tym, że dziewięć razy na dziesięć jest to całkowicie niewłaściwe, StringBuilderzamiast konkatenacji.
Randolpho,

84
Wolę ciąg. Pusty, głównie dlatego, że jest bardziej wyraźny. Są też sytuacje, w których wizualne odróżnienie „” może być trudniejsze i „” ”. W końcu, jak zauważyli inni, jest to tylko jedna z tych bezsensownych rzeczy w stylu, które dają nam paszę do sporu, gdy nudzimy się prawdziwą pracą. =)
JohnFx

@Nodel M: Jeśli chodzi o czas kompilacji, zakładam, że jeśli w 2 różnych plikach źródłowych o tej samej wartości ciągu są zdefiniowane 2 literały łańcuchowe, to gdy kompilator trafi na drugi, musi dokonać pewnego sprawdzenia, aby dowiedzieć się, co to jest „ hej, już wiem o tym sznurku z powrotem tutaj ". Nie jestem wprawdzie ekspertem od kompilatora Java, ale jak to NIE mogło być prawdą? I pomyślałem, że pominięcie tej kontroli spowodowałoby niewielką poprawę czasów kompilacji.
Tom Tresansky,

@Tom - Uważam, że internowanie ciągów odbywa się w czasie wykonywania, a nie w czasie kompilacji, więc tak naprawdę jeśli pusty ciąg jest ciągły w innym pliku, kompilator musi odwoływać się do tej klasy, aby rozpoznać literał ciągu.
Noel M

1
@Randolpho Korzystając z konkatenacji łańcuchów, faktycznie używasz StringBuilder pod maską.
whiskeysierra,

133

Posługiwać się org.apache.commons.lang.StringUtils.EMPTY


29
Wygląda o wiele ładniej i jest łatwiejszy do odczytania niż pusty „”. Mam nadzieję, że nie tylko ja.
Lakatos Gyula

2
@LatatosGyula - Myślę, że to może być (tylko ty). Biegli programiści Java nie mają problemów z czytaniem ""... i większość z nich prawdopodobnie sprzeciwiłaby się głośno używaniu, z EMPTYwyjątkiem szczególnych sytuacji, w których EMPTYma znaczenie specyficzne dla domeny. (I w takich przypadkach prawdopodobnie jest bardziej odpowiednia nazwa.)
Stephen C

14
@LakatosGyula To nie tylko ty. Przeszedłem z programowania Java do .NET, a String.Empty był funkcją, którą z przyjemnością znalazłem w frameworku. Podoba mi się wyraźny charakter tego pustego zestawu cytatów.
yohohoho

63
@StephenC Kiedy widzę pusty „”, w mojej głowie pojawia się pierwsza myśl, że to błąd, ktoś nie dokończył funkcji itp. Z String.EMPTY Wiem dokładnie, że deweloper zamierzał zwrócić pusty ciąg.
Lakatos Gyula

1
Pomocny również w tych wszystkich czasach, gdy liniowiec mówi „użyj nazwanej stałej zamiast bla bla bla”. Każdy programista wie, że „” nie jest magią, ale lepiej nie wyjaśniać klientowi.
LizH

28

Jeśli chcesz porównać z pustym ciągiem, nie martwiąc się o wartości zerowe, możesz wykonać następujące czynności.

if ("".equals(text))

Ostatecznie powinieneś zrobić to, co według ciebie jest najczystsze. Większość programistów zakłada, że ​​„” oznacza pusty ciąg, a nie ciąg, o którym ktoś zapomniał włożyć coś.

Jeśli uważasz, że istnieje przewaga wydajności, powinieneś ją przetestować. Jeśli nie uważasz, że warto go przetestować, to dobra wskazówka, że ​​naprawdę nie jest tego wart.

Wygląda na to, że próbujesz rozwiązać problem, który został rozwiązany, gdy język został zaprojektowany ponad 15 lat temu.


1
Jestem spóźniony na przyjęcie, ale ponieważ ciągi Java są niezmienne, uważam, że wszystkie puste ciągi w JVM są po prostu różnymi odniesieniami do tego samego obiektu String. Więc po prostu poprawne jest również: if ("" == text)
Ajoy Bhatia

12
@AjoyBhatia Problem polega na tym, że możesz tworzyć nowe puste ciągi. if ("" == new String())to fałsz. Lepszy testif(text.isEmpty())
Peter Lawrey,

1
@AjoyBhatia - Tylko jeśli łańcuchy są internowane. stackoverflow.com/questions/10578984/what-is-string-interning
Davor

9

Jeśli naprawdę chcesz stałej String.EMPTY, możesz utworzyć statyczną klasę końcową narzędzia o nazwie „Stałe” (na przykład) w swoim projekcie. Ta klasa zachowa twoje stałe, w tym pusty ciąg ...

W tym samym pomyśle możesz stworzyć ZERO, JEDNĄ stałą int ... która nie istnieje w klasie Integer, ale jak skomentowałem, pisanie i czytanie byłoby utrudnieniem:

for(int i=Constants.ZERO; ...) {
    if(myArray.length > Constants.ONE) {
        System.out.println("More than one element");
    }
}

Itp.


8

Apache StringUtils rozwiązuje również ten problem.

Wady pozostałych opcji:

  • isEmpty () - nie jest zerowo bezpieczny. Jeśli ciąg jest pusty, zgłasza NPE
  • length () == 0 - znowu nie jest zerowo bezpieczny. Nie uwzględnia również ciągów białych znaków.
  • Porównanie ze stałą EMPTY - może nie być zerowo bezpieczne. Problem z białymi znakami

Granted StringUtils to kolejna biblioteka do przeciągania, ale działa bardzo dobrze i oszczędza mnóstwo czasu i kłopotów ze sprawdzaniem wartości zerowych lub z wdzięczną obsługą NPE.


3
tak ... wydaje się, że jedynym bezpiecznym rozwiązaniem jest okropny stan Yoda: "".equals(s)?
Lie Ryan,

8

Nie mów tylko, że „pula pamięci ciągów znaków jest ponownie używana w postaci dosłownej, sprawa zamknięta”. Nie chodzi o to, co kompilatory robią pod maską. Pytanie jest rozsądne, zwłaszcza biorąc pod uwagę liczbę głosów, które otrzymało.

Chodzi o symetrię , bez której interfejsy API są trudniejsze w użyciu dla ludzi. Wczesne zestawy SDK Java notorycznie ignorowały tę zasadę, a teraz jest już trochę za późno. Oto kilka przykładów na mojej głowie. Zapraszam do zapoznania się z „ulubionym” przykładem:

  • BigDecimal.ZERO, ale bez AbstractCollection.EMPTY, String.EMPTY
  • Array.length but List.size ()
  • List.add (), Set.add () ale Map.put (), ByteBuffer.put () i nie zapominajmy StringBuilder.append (), Stack.push ()

Nawet jeśli nazwałeś parametr List length (), nadal potrzebujesz nawiasu, ponieważ jest to metoda. Array.length jest publiczną zmienną końcową, która działa tylko dlatego, że tablice są niezmienne. Więc nadal będziesz mieć Array.length i List.length (). Twierdziłbym, że jest to bardziej mylące i podatne na błędy. Jeśli chodzi o .append () i .push (), podczas gdy wykonują podobne zadania, myślę, że odpowiednio nazwali. Dołączanie ciągu jest dokładnie tym, co robisz, ale nie „dołączasz” stosu, naciskasz i wyskakujesz. A StringBuilder.push () sugeruje StringBuilder.pop (), co nie jest możliwe.
Craig Parton

Pochodzące z szablonów / ogólnych, spójne interfejsy pomagają również w algorytmach. Jeśli algorytm potrzebuje długości kolekcji, dbamy o długość (T) lub T.length (). Podobnie dodawanie na końcu stosu, listy lub łańcucha można wykonać za pomocą uniwersalnego add () lub append (). Wspomniałeś, że tablica w Javie jest niezmienna / wbudowana z ujawnioną właściwością długości. To dobrze, nie oznacza to, że kompilator nie może obsłużyć ani wygenerować kodu dla długości (T) lub T.length (). Kotlin generuje szereg wewnętrznych metod dla różnych przypadków.
Sławomir

Ale konsekwentnie nazywana metoda length () pozwala nam tylko sprawdzić długość. Jak to jest użyteczne? Jeśli Twoim celem jest wyodrębnienie list i tablic w celu umożliwienia korzystania z interfejsu w jakiś sposób, potrzebujesz również spójnego sposobu odczytu lub zapisu danych. Teraz musisz wygenerować metody get (), set () i add (). Zasadniczo tworzysz mniej funkcjonalny widok listy tablicy. Ponieważ Arrays.asList () jest dostępny, łatwy w użyciu i lekki, po co wymyślać koło ponownie? Tablice, listy, StringBuilders i stosy mają określony cel. Wydaje się, że lepiej zaprojektować interfejs tak, aby był jak najlepiej dopasowany.
Craig Parton

5

Te wszystkie "" literały są tym samym przedmiotem. Po co robić całą tę dodatkową złożoność? Pisanie jest dłuższe i mniej jasne (koszt kompilatora jest minimalny). Ponieważ łańcuchy Javy są niezmiennymi obiektami, nigdy nie ma potrzeby ich rozróżniania, chyba że ze względu na wydajność, ale z pustym dosłownym łańcuchem nie jest to wielka sprawa.

Jeśli naprawdę chcesz EmptyStringstałej, zrób to sam. Ale wystarczy, że zachęci jeszcze więcej pełnego kodu; nigdy nie przyniesie to żadnych korzyści.


27
x = String.Emptyprzekazuje cel lepiej niż x = "". To ostatnie może być przypadkowym pominięciem. Stwierdzenie, że nigdy nie ma żadnych korzyści, jest błędne.
Jeffrey L Whitledge,

@Jeffrey: Nie sądzę, że się szczególnie zgadzam. To jedna z tych rzeczy, w których, jak sądzę, nie ma twardych i szybkich zasad.
Donal Fellows,

Tak, należy zaznaczyć, że kompilator Java sprawdza, czy literały łańcuchowe już istnieją, przed utworzeniem nowej instancji w puli ciągów.
rds

1
@Jeffrey - wiedząc, że jest to bardzo stara i subiektywna dyskusja. x = String.Emptyprzekazuje zamiar, prawda. Przypuśćmy jednak, że język zapewnia stałą wartość String.Empty, ale kiedy napotykasz x = "", nadal wiesz dokładnie tyle o intencji, jak gdyby nie było takiej stałej. Musisz mieć gwarancję, że wszystkie miejsca w kodzie Java świata, w których zamierzony był pusty ciąg, nie będą używane ""do uzyskania wspomnianych informacji. Jak na ironię, C # używa stałej i zachęca do jej używania, więc jak powiedziałem, wiem, że jest to bardzo pozytywna dyskusja.
chiccodoro

@chiccodoro - Tak, to prawda. Właśnie dlatego literał pusty ciąg znaków ""powinien być nielegalny, aby wykluczyć wypadki. Żartuję!
Jeffrey L Whitledge,


3

Rozumiem, że za każdym razem, gdy wpisuję literał „”, do puli ciągów odwołuje się ten sam obiekt String.
Nie ma takiej gwarancji. I nie możesz na nim polegać w swojej aplikacji, decyzja należy do jvm.

czy też twórcy języków po prostu nie podzielili się moimi opiniami?
Tak. Wydaje mi się, że sprawa ma bardzo niski priorytet.


6
Nie ma takiej gwarancji ... Cóż, JLS stwierdza, że tak powinno być.
Tim Stone,

@ Czas Nie, chyba że wykonasz połączenie „intern”. Łatwo jest programowo skonstruować dwa równe duże ciągi i sprawdzić.
Nikita Rybak,

@Tim Na przykład powtórz + + = "a"; 100 razy, zrób to samo zb i sprawdź.
Nikita Rybak,

5
Masz rację, ale to, co opisałeś, nie jest dosłownym ciągiem ani wyrażeniem, którego wynik można zagwarantować w czasie kompilacji (np. String username = "Bob" + " " + "Smith";). Ciągi utworzone programowo nie mają gwarancji internowania, chyba że wyraźnie zadzwonisz intern()tak, jak powiedziałeś. Scenariusz OP opisuje jednak użycie literału pustego ciągu w ""całym kodzie, co jest przypadkiem, w którym nastąpi automatyczne internowanie.
Tim Stone,

@Tim String a = ""; for(int i = 0; i < 100; i++) {a += "a";} String b = ""; for(int i = 0; i < 100; i++) {b += "b";} a.intern(); b.intern();Teraz ai bpunkt w tej samej lokalizacji w pamięci PermGen. Zobacz ten artykuł
1ac0

1

Późna odpowiedź, ale myślę, że dodaje coś nowego do tego tematu.

Żadna z poprzednich odpowiedzi nie odpowiedziała na pierwotne pytanie. Niektórzy próbowali uzasadnić brak stałej, podczas gdy inni pokazali sposoby radzenia sobie z brakiem stałej. Ale nikt nie przedstawił przekonującego uzasadnienia dla korzyści stałej, więc jej brak nadal nie jest właściwie wyjaśniony.

Przydałaby się stała, ponieważ zapobiegałaby niezauważeniu niektórych błędów kodu.

Powiedz, że masz dużą bazę kodu z setkami odniesień do „”. Ktoś modyfikuje jeden z nich podczas przewijania kodu i zmienia go na „”. Taka zmiana miałaby duże szanse na niezauważenie w produkcji, w którym to momencie może spowodować problem, którego źródło będzie trudne do wykrycia.

OTOH, stała biblioteki o nazwie EMPTY, jeśli podlega temu samemu błędowi, wygenerowałaby błąd kompilatora dla czegoś takiego jak EM PTY.

Zdefiniowanie własnej stałej jest jeszcze lepsze. Ktoś nadal mógłby przez pomyłkę zmienić jego inicjalizację, ale z powodu jego szerokiego zastosowania wpływ takiego błędu byłby o wiele trudniejszy do zauważenia niż błąd w pojedynczym przypadku użycia.

Jest to jedna z ogólnych korzyści płynących z używania stałych zamiast wartości dosłownych. Ludzie zwykle rozpoznają, że użycie stałej dla wartości używanej w kilkudziesięciu miejscach pozwala łatwo zaktualizować tę wartość tylko w jednym miejscu. Rzadziej uznaje się, że zapobiega to przypadkowej modyfikacji tej wartości, ponieważ taka zmiana byłaby widoczna wszędzie. Tak więc „” jest krótszy niż PUSTY, ale PUSTY jest bezpieczniejszy w użyciu niż „”.

Wracając do pierwotnego pytania, możemy jedynie spekulować, że projektanci języków prawdopodobnie nie zdawali sobie sprawy z korzyści wynikających z zapewnienia stałych dla często używanych wartości literalnych. Mamy nadzieję, że pewnego dnia zobaczymy stałe ciągów dodane w Javie.


-16

Dla tych, którzy twierdzą ""i String.Emptysą wymienni lub co ""jest lepsze, bardzo się mylisz.

Za każdym razem, gdy robisz coś takiego, jak myVariable = ""; tworzysz instancję obiektu. Jeśli obiekt String języka Java miałby PUSTĄ stałą publiczną, istniałaby tylko 1 instancja obiektu „”

Np .: -

String.EMPTY = ""; //Simply demonstrating. I realize this is invalid syntax

myVar0 = String.EMPTY;
myVar1 = String.EMPTY;
myVar2 = String.EMPTY;
myVar3 = String.EMPTY;
myVar4 = String.EMPTY;
myVar5 = String.EMPTY;
myVar6 = String.EMPTY;
myVar7 = String.EMPTY;
myVar8 = String.EMPTY;
myVar9 = String.EMPTY;

10 (11 łącznie z String.EMPTY) Wskaźniki do 1 obiektu

Lub: -

myVar0 = "";
myVar1 = "";
myVar2 = "";
myVar3 = "";
myVar4 = "";
myVar5 = "";
myVar6 = "";
myVar7 = "";
myVar8 = "";
myVar9 = "";

10 wskaźników do 10 obiektów

Jest to nieefektywne i w przypadku dużej aplikacji może być znaczące.

Być może kompilator Java lub środowisko wykonawcze jest wystarczająco wydajne, aby automatycznie wskazywać wszystkie wystąpienia „” na to samo wystąpienie, ale może nie i wymaga dodatkowego przetwarzania, aby dokonać tego ustalenia.


9
Niepoprawnie, zgodnie z stackoverflow.com/questions/1881922/... , ciąg „” zostanie ponownie użyty z puli ciągów.
RealHowTo

1
Stwierdziłem, że może ponownie użyć tego samego obiektu, a jeśli tak, to nadal jest mniej wydajny, ponieważ musi znaleźć ten obiekt (w puli ciągów), więc jak się mylę? Niezależnie od tego istnieje kilka powodów, dla których String.Empty jest lepszy, w tym zapobieganie błędom, takim jak myVar = ""; i czytelność, a także poprawę wydajności, o której już wspomniałem. Dobrą praktyką jest używanie stałych zamiast tworzenia literałów łańcuchowych, jeśli nie z innego powodu; łatwiej jest utrzymać kod.
Antony Booth,

1
Wątpię, czy Twój argument wydajności jest prawidłowy, ponieważ JLS mówi, że stała będzie traktowana jako literał w czasie kompilacji ( docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10. 5 ). Czytelność jest lepszym argumentem.
RealHowTo

3
@AntonySmith - Wydaje mi się, że musisz trochę więcej nauczyć się języka Java, a może już znasz swój błąd. Ciągi Java są niezmienne i znajdują się w puli. Zatem w JVM jest tylko JEDEN obiekt Ciąg dla „”, bez względu na to, ile razy został znaleziony w kodzie. Możesz sprawdzić, czy łańcuch jest pusty, wykonującif (text == "")
Ajoy Bhatia,

2
Źle. Ten komentarz powinien zostać usunięty.
Elad Tabak,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.