Java Language
Smyczki
Szukaj…
Wprowadzenie
Ciągi ( java.lang.String
) to fragmenty tekstu przechowywane w twoim programie. Ciągi nie są prymitywnym typem danych w Javie , jednak są bardzo powszechne w programach Java.
W Javie ciągi są niezmienne, co oznacza, że nie można ich zmienić. (Kliknij tutaj, aby uzyskać dokładniejsze wyjaśnienie niezmienności.)
Uwagi
Ponieważ ciągi Java są niezmienne , wszystkie metody, które manipulować String
powróci nowy String
obiektu. Nie zmienia oryginalnego String
. Obejmuje to metody podciągania i zastępowania, których programiści C i C ++ oczekiwaliby mutować docelowy obiekt String
.
Użyj StringBuilder
zamiast String
jeśli chcesz połączyć więcej niż dwa obiekty String
których wartości nie można ustalić w czasie kompilacji. Ta technika jest bardziej wydajna niż tworzenie nowych obiektów String
i łączenie ich, ponieważ StringBuilder
można modyfikować.
StringBuffer
może być również użyty do łączenia obiektów String
. Jednak ta klasa jest mniej wydajna, ponieważ została zaprojektowana pod kątem bezpieczeństwa wątków i uzyskuje muteks przed każdą operacją. Ponieważ prawie nigdy nie potrzebujesz bezpieczeństwa wątków podczas łączenia łańcuchów, najlepiej jest użyć StringBuilder
.
Jeśli możesz wyrazić konkatenację łańcucha jako pojedyncze wyrażenie, lepiej użyć operatora +
. Kompilator Java skonwertuje wyrażenie zawierające +
konkatenacje na wydajną sekwencję operacji za pomocą String.concat(...)
lub StringBuilder
. Porada dotycząca używania StringBuilder
jawna tylko wtedy, gdy konkatenacja obejmuje wiele wyrażeń.
Nie przechowuj poufnych informacji w ciągach. Jeśli ktoś będzie w stanie uzyskać zrzut pamięci działającej aplikacji, będzie mógł znaleźć wszystkie istniejące obiekty String
i odczytać ich zawartość. Obejmuje to obiekty String
które są nieosiągalne i oczekują na wyrzucanie elementów bezużytecznych. Jeśli jest to problem, musisz wyczyścić poufne dane ciągu, jak tylko to zrobisz. Nie możesz tego zrobić z obiektami String
ponieważ są one niezmienne. Dlatego wskazane jest użycie obiektów char[]
do przechowywania poufnych danych znaków i wyczyszczenie ich (np. Zastąpienie ich znakami '\000'
) po zakończeniu.
Wszystkie String
przypadki są tworzone na stercie, nawet przypadki, które odpowiadają napisowych. Specjalną cechą literałów łańcuchowych jest to, że JVM zapewnia, że wszystkie literały, które są równe (tj. Składają się z tych samych znaków), są reprezentowane przez pojedynczy obiekt String
(takie zachowanie jest określone w JLS). Jest to realizowane przez ładowarki klasy JVM. Gdy moduł ładujący klasy ładuje klasę, skanuje w poszukiwaniu literałów ciągów, które są używane w definicji klasy, za każdym razem, gdy je widzi, sprawdza, czy w puli ciągów znaków znajduje się już rekord dla tego literału (używając literału jako klucza) . Jeśli istnieje już wpis dla literału, używane jest odwołanie do instancji String
przechowywanej jako para dla tego literału. W przeciwnym razie tworzona jest nowa instancja String
a odwołanie do instancji jest przechowywane dla literału (używanego jako klucz) w puli łańcuchów. (Zobacz także internowanie ciągów ).
Pula ciągów jest przechowywana na stercie Java i podlega normalnemu wyrzucaniu elementów bezużytecznych.
W wersjach Java wcześniejszych niż Java 7 pula ciągów była przechowywana w specjalnej części sterty znanej jako „PermGen”. Ta część była zbierana tylko od czasu do czasu.
W Javie 7 pula ciągów została przeniesiona z „PermGen”.
Zauważ, że literały łańcuchowe są domyślnie osiągalne z dowolnej metody, która ich używa. Oznacza to, że odpowiednie obiekty String
mogą być odśmiecane tylko wtedy, gdy sam kod jest odśmiecany.
Do wersji Java 8 obiekty String
są implementowane jako tablica znaków UTF-16 (2 bajty na znak). W Javie 9 jest propozycja, aby zaimplementować String
znaków jako tablicę bajtów z polem flagi kodowania, aby zauważyć, czy ciąg znaków jest kodowany jako bajty (LATIN-1) lub znaki (UTF-16).
Porównywanie ciągów
Aby porównać ciągi equalsIgnoreCase
kątem równości, należy użyć metod equals
lub equalsIgnoreCase
obiektu String.
Na przykład, poniższy fragment kodu będzie ustalić, czy te dwie instancje String
są równe dla wszystkich znaków:
String firstString = "Test123";
String secondString = "Test" + 123;
if (firstString.equals(secondString)) {
// Both Strings have the same content.
}
Ten przykład porówna je, niezależnie od ich przypadku:
String firstString = "Test123";
String secondString = "TEST123";
if (firstString.equalsIgnoreCase(secondString)) {
// Both Strings are equal, ignoring the case of the individual characters.
}
Pamiętaj, że equalsIgnoreCase
nie pozwala na określenie equalsIgnoreCase
Locale
. Na przykład, jeśli porównasz dwa słowa "Taki"
i "TAKI"
w języku angielskim, są one równe; jednak w języku tureckim są one różne (w języku tureckim mała litera I
to ı
). W takich przypadkach rozwiązaniem jest konwersja obu ciągów znaków na małe litery (lub wielkie litery) za pomocą Locale
a następnie porównanie z equals
.
String firstString = "Taki";
String secondString = "TAKI";
System.out.println(firstString.equalsIgnoreCase(secondString)); //prints true
Locale locale = Locale.forLanguageTag("tr-TR");
System.out.println(firstString.toLowerCase(locale).equals(
secondString.toLowerCase(locale))); //prints false
Nie używaj operatora == do porównywania ciągów
O ile nie możesz zagwarantować, że wszystkie łańcuchy zostały internowane (patrz poniżej), nie powinieneś używać operatorów ==
lub !=
Do porównywania łańcuchów. Operatory te faktycznie testują odwołania, a ponieważ wiele obiektów String
może reprezentować ten sam String, może to dać niewłaściwą odpowiedź.
Zamiast tego użyj metody String.equals(Object)
, która porówna obiekty String na podstawie ich wartości. Aby uzyskać szczegółowe wyjaśnienie, patrz Pitfall: użycie == do porównania ciągów .
Porównywanie ciągów w instrukcji switch
Od wersji Java 1.7 możliwe jest porównanie zmiennej String z literałami w instrukcji switch
. Upewnij się, że ciąg nie ma wartości null, w przeciwnym razie zawsze będzie NullPointerException
. Wartości są porównywane za pomocą String.equals
, tzn. String.equals
i String.equals
litery.
String stringToSwitch = "A";
switch (stringToSwitch) {
case "a":
System.out.println("a");
break;
case "A":
System.out.println("A"); //the code goes here
break;
case "B":
System.out.println("B");
break;
default:
break;
}
Porównywanie ciągów ze stałymi wartościami
Porównując String
ze stałą wartością, możesz umieścić stałą wartość po lewej stronie NullPointerException
equals
aby upewnić się, że nie otrzymasz NullPointerException
jeśli drugi ciąg ma null
.
"baz".equals(foo)
Podczas gdy foo.equals("baz")
zgłosi NullPointerException
jeśli foo
ma null
, to "baz".equals(foo)
ma wartość false
.
Bardziej czytelną alternatywą jest użycie Objects.equals()
, która sprawdza Objects.equals(foo, "baz")
oba parametry: Objects.equals(foo, "baz")
.
( Uwaga: Można dyskutować, czy lepiej jest unikać NullPointerExceptions
w ogóle, czy pozwolić im się zdarzyć, a następnie naprawić pierwotną przyczynę; patrz tutaj i tutaj . Z pewnością nazywanie strategii unikania „najlepszą praktyką” nie jest uzasadnione.)
Kolejność ciągów
Klasa String
implementuje Comparable<String>
za pomocą metody String.compareTo
(jak opisano na początku tego przykładu). To sprawia, że w naturalnym uporządkowaniu obiektów String
rozróżniana jest wielkość liter. Klasa String
zapewnia stałą Comparator<String>
nazwie CASE_INSENSITIVE_ORDER
odpowiednią do sortowania bez rozróżniania wielkości liter.
Porównywanie z internowanymi ciągami
Specyfikacja języka Java ( JLS 3.10.6 ) stwierdza, co następuje:
„Ponadto literał łańcuchowy zawsze odnosi się do tego samego wystąpienia klasy
String
. Jest tak, ponieważ literały łańcuchowe - lub, bardziej ogólnie, łańcuchy, które są wartościami wyrażeń stałych - są internalizowane , aby dzielić unikalne instancje, przy użyciu metodyString.intern
. ”
Oznacza to, że można bezpiecznie porównywać odniesienia do dwóch literałów łańcuchowych za pomocą ==
. Ponadto to samo dotyczy odwołań do obiektów String
, które zostały utworzone przy użyciu metody String.intern()
.
Na przykład:
String strObj = new String("Hello!");
String str = "Hello!";
// The two string references point two strings that are equal
if (strObj.equals(str)) {
System.out.println("The strings are equal");
}
// The two string references do not point to the same object
if (strObj != str) {
System.out.println("The strings are not the same object");
}
// If we intern a string that is equal to a given literal, the result is
// a string that has the same reference as the literal.
String internedStr = strObj.intern();
if (internedStr == str) {
System.out.println("The interned string and the literal are the same object");
}
Za kulisami mechanizm internowania utrzymuje tablicę skrótów, która zawiera wszystkie internowane ciągi, które są nadal osiągalne . Kiedy wywołujesz intern()
na String
, metoda wyszukuje obiekt w tablicy hash:
- Jeśli ciąg zostanie znaleziony, wówczas ta wartość jest zwracana jako ciąg wewnętrzny.
- W przeciwnym razie kopia łańcucha zostanie dodana do tabeli mieszającej, a ten łańcuch zostanie zwrócony jako ciąg wewnętrzny.
Możliwe jest użycie internowania, aby umożliwić porównywanie ciągów za pomocą ==
. Istnieją jednak poważne problemy z tym; zobacz Pitfall - Interninging strings, abyś mógł użyć == to zły pomysł na szczegóły. W większości przypadków nie jest to zalecane.
Zmiana wielkości liter w ciągu znaków
String
typu zawiera dwa sposoby przekształcenia łańcuchy pomiędzy górną obudową oraz dolną obudową:
-
toUpperCase
do konwersji wszystkich znaków na wielkie litery -
toLowerCase
do konwersji wszystkich znaków na małe litery
Obie metody zwracają przekonwertowane ciągi jako nowe wystąpienia String
: oryginalne obiekty String
nie są modyfikowane, ponieważ String
jest niezmienny w Javie. Więcej informacji na temat niezmienności: Niezmienność ciągów w Javie
String string = "This is a Random String";
String upper = string.toUpperCase();
String lower = string.toLowerCase();
System.out.println(string); // prints "This is a Random String"
System.out.println(lower); // prints "this is a random string"
System.out.println(upper); // prints "THIS IS A RANDOM STRING"
Te metody nie mają wpływu na znaki niealfabetyczne, takie jak cyfry i znaki interpunkcyjne. Należy pamiętać, że te metody mogą również niepoprawnie radzić sobie z niektórymi znakami Unicode pod pewnymi warunkami.
Uwaga : Te metody są wrażliwe na ustawienia regionalne i mogą dawać nieoczekiwane wyniki, jeśli zostaną użyte w ciągach znaków, które mają być interpretowane niezależnie od ustawień regionalnych. Przykładami są identyfikatory języka programowania, klucze protokołu i znaczniki HTML
.
Na przykład "TITLE".toLowerCase()
w języku tureckim zwraca „ tıtle
”, gdzie ı (\u0131)
jest ı (\u0131)
LATIN SMALL LETTER DOTLESS I. Aby uzyskać prawidłowe wyniki dla ciągów niewrażliwych na ustawienia regionalne, należy przekazać Locale.ROOT
jako parametr do odpowiedniej metody konwersji wielkości liter (np. toLowerCase(Locale.ROOT)
lub toUpperCase(Locale.ROOT)
).
Mimo że użycie Locale.ENGLISH
jest również poprawne w większości przypadków, niezmiennym językiem jest Locale.ROOT
.
Szczegółową listę znaków Unicode wymagających specjalnej obudowy można znaleźć na stronie internetowej Konsorcjum Unicode .
Zmienianie wielkości liter określonego znaku w ciągu ASCII:
Aby zmienić wielkość liter określonego znaku ciągu ASCII, można zastosować następujący algorytm:
Kroki:
- Zadeklaruj ciąg.
- Wpisz ciąg.
- Przekształć ciąg w tablicę znaków.
- Wpisz znak, który chcesz przeszukać.
- Wyszukaj znak w tablicy znaków.
- Jeśli znaleziono, sprawdź, czy znak jest pisany małymi lub dużymi literami.
- Jeśli wielkie litery, dodaj 32 do kodu ASCII znaku.
- Jeśli małe litery, odejmij 32 od kodu ASCII znaku.
- Zmień oryginalny znak z tablicy znaków.
- Konwertuj tablicę znaków z powrotem na ciąg.
Voila, Sprawa postaci została zmieniona.
Przykładem kodu dla algorytmu jest:
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the String");
String s = scanner.next();
char[] a = s.toCharArray();
System.out.println("Enter the character you are looking for");
System.out.println(s);
String c = scanner.next();
char d = c.charAt(0);
for (int i = 0; i <= s.length(); i++) {
if (a[i] == d) {
if (d >= 'a' && d <= 'z') {
d -= 32;
} else if (d >= 'A' && d <= 'Z') {
d += 32;
}
a[i] = d;
break;
}
}
s = String.valueOf(a);
System.out.println(s);
Znajdowanie ciągu w innym ciągu
Aby sprawdzić, czy określony Ciąg a
jest zawarty w String.contains()
b
czy nie, możemy użyć metody String.contains()
o następującej składni:
b.contains(a); // Return true if a is contained in b, false otherwise
Za String.contains()
metody String.contains()
można sprawdzić, czy w CharSequence
można znaleźć CharSequence
. Metoda szuka ciągu a
w ciągu b
z rozróżnianiem wielkości liter.
String str1 = "Hello World";
String str2 = "Hello";
String str3 = "helLO";
System.out.println(str1.contains(str2)); //prints true
System.out.println(str1.contains(str3)); //prints false
Aby znaleźć dokładną pozycję, w której łańcuch zaczyna się w innym String.indexOf()
, użyj String.indexOf()
:
String s = "this is a long sentence";
int i = s.indexOf('i'); // the first 'i' in String is at index 2
int j = s.indexOf("long"); // the index of the first occurrence of "long" in s is 10
int k = s.indexOf('z'); // k is -1 because 'z' was not found in String s
int h = s.indexOf("LoNg"); // h is -1 because "LoNg" was not found in String s
String.indexOf()
sposób wraca pierwszy indeks char
lub String
w innym String
. Metoda zwraca -1
jeśli nie zostanie znaleziona.
Uwaga : W String.indexOf()
rozróżniana jest String.indexOf()
liter.
Przykład wyszukiwania ignorujący wielkość liter:
String str1 = "Hello World";
String str2 = "wOr";
str1.indexOf(str2); // -1
str1.toLowerCase().contains(str2.toLowerCase()); // true
str1.toLowerCase().indexOf(str2.toLowerCase()); // 6
Uzyskiwanie długości ciągu
Aby uzyskać długość obiektu String
, wywołaj na nim metodę length()
. Długość jest równa liczbie jednostek kodu (znaków) UTF-16 w ciągu.
String str = "Hello, World!";
System.out.println(str.length()); // Prints out 13
char
w ciągu znaków UTF-16 wartość. Punkty kodowe Unicode, których wartości to ≥ 0x1000 (na przykład większość emoji) używają dwóch pozycji znaków. Aby policzyć codepoints Unicode w ciągu, niezależnie od tego, czy każdy punkt kodowy mieści się w UTF-16 char
wartości, można użyć codePointCount
metodę:
int length = str.codePointCount(0, str.length());
Możesz także użyć strumienia punktów kodowych, począwszy od Java 8:
int length = str.codePoints().count();
Podciągi
String s = "this is an example";
String a = s.substring(11); // a will hold the string starting at character 11 until the end ("example")
String b = s.substring(5, 10); // b will hold the string starting at character 5 and ending right before character 10 ("is an")
String b = s.substring(5, b.length()-3); // b will hold the string starting at character 5 ending right before b' s lenght is out of 3 ("is an exam")
Podciągi można również stosować do wycinania i dodawania / zastępowania znaku w jego oryginalnym ciągu. Na przykład napotkałeś chińską datę zawierającą chińskie znaki, ale chcesz ją zapisać jako dobrze sformatowany ciąg dat.
String datestring = "2015年11月17日"
datestring = datestring.substring(0, 4) + "-" + datestring.substring(5,7) + "-" + datestring.substring(8,10);
//Result will be 2015-11-17
Podciąg sposób wyodrębnia kawałek String
. Kiedy pod warunkiem jeden parametr, parametr jest początek i kawałek rozciąga się aż do końca String
. Gdy podano dwa parametry, pierwszy parametr jest znakiem początkowym, a drugi parametr jest indeksem znaku zaraz po zakończeniu (znak przy indeksie nie jest uwzględniany). Łatwym sposobem sprawdzenia jest odjęcie pierwszego parametru od drugiego, co powinno dać oczekiwaną długość ciągu.
W JDK <7u6 WERSJE substring
metoda instancję String
że akcja taka sama podkład char[]
jako oryginalny String
i wewnętrzne offset
i count
pola ustawione na początku wyników i długości. Takie udostępnianie może powodować wycieki pamięci, którym można zapobiec, wywołując new String(s.substring(...))
celu wymuszenia utworzenia kopii, po czym char[]
może zostać wyrzucony.
Z JDK 7u6 substring
sposobu zawsze kopiuje cały Bazowy char[]
macierzy, przez co złożoność liniowym w porównaniu do poprzedniego, ale ciągłego zapewniając brak przecieków pamięci w tym samym czasie.
Uzyskiwanie n-tego znaku w ciągu
String str = "My String";
System.out.println(str.charAt(0)); // "M"
System.out.println(str.charAt(1)); // "y"
System.out.println(str.charAt(2)); // " "
System.out.println(str.charAt(str.length-1)); // Last character "g"
Aby uzyskać n-ty znak w ciągu, po prostu zadzwoń charAt(n)
na String
, gdzie n
jest indeksem charakteru chcesz odzyskać
UWAGA: indeks n
zaczyna się od 0
, więc pierwszy element ma wartość n = 0.
Nowy niezależny od platformy separator linii
Ponieważ nowy separator linii różni się w zależności od platformy (np. \n
w systemach uniksowych lub \r\n
w systemie Windows), często konieczne jest posiadanie niezależnego od platformy sposobu dostępu do niego. W Javie można go pobrać z właściwości systemowej:
System.getProperty("line.separator")
Ponieważ nowy separator wierszy jest tak często potrzebny, w Javie 7 metoda skrótu zwraca dokładnie taki sam wynik jak powyższy kod:
System.lineSeparator()
Uwaga : Ponieważ jest bardzo mało prawdopodobne, aby nowy separator linii zmieniał się podczas wykonywania programu, dobrym pomysłem jest przechowywanie go w statycznej zmiennej końcowej zamiast pobierania go z właściwości systemowej za każdym razem, gdy jest potrzebny.
Korzystając z String.format
, użyj %n
zamiast \n
lub '\ r \ n', aby wyprowadzić niezależny od platformy separator linii.
System.out.println(String.format('line 1: %s.%nline 2: %s%n', lines[0],lines[1]));
Dodanie metody toString () dla obiektów niestandardowych
Załóżmy, że zdefiniowałeś następującą klasę Person
:
public class Person {
String name;
int age;
public Person (int age, String name) {
this.age = age;
this.name = name;
}
}
Jeśli utworzysz instancję nowego obiektu Person
:
Person person = new Person(25, "John");
a później w kodzie używasz następującej instrukcji, aby wydrukować obiekt:
System.out.println(person.toString());
otrzymasz wynik podobny do następującego:
Person@7ab89d
Jest to wynik implementacji metody toString()
zdefiniowanej w klasie Object
, nadklasie Person
. Dokumentacja Object.toString()
stwierdza:
Metoda toString dla klasy Object zwraca łańcuch składający się z nazwy klasy, której obiekt jest instancją, znaku at @ `i niepodpisanej szesnastkowej reprezentacji kodu skrótu obiektu. Innymi słowy, ta metoda zwraca ciąg znaków równy wartości:
getClass().getName() + '@' + Integer.toHexString(hashCode())
Aby uzyskać znaczące wyniki, należy zastąpić metodę toString()
:
@Override
public String toString() {
return "My name is " + this.name + " and my age is " + this.age;
}
Teraz wyjście będzie:
My name is John and my age is 25
Możesz także pisać
System.out.println(person);
W rzeczywistości println()
domyślnie wywołuje metodę toString
na obiekcie.
Dzielenie strun
Możesz podzielić String
na określony znak ograniczający lub wyrażenie regularne , możesz użyć metody String.split()
, która ma następującą sygnaturę:
public String[] split(String regex)
Zauważ, że znak ograniczający lub wyrażenie regularne jest usuwany z wynikowej tablicy łańcuchowej.
Przykład użycia znaku ograniczającego:
String lineFromCsvFile = "Mickey;Bolton;12345;121216";
String[] dataCells = lineFromCsvFile.split(";");
// Result is dataCells = { "Mickey", "Bolton", "12345", "121216"};
Przykład użycia wyrażenia regularnego:
String lineFromInput = "What do you need from me?";
String[] words = lineFromInput.split("\\s+"); // one or more space chars
// Result is words = {"What", "do", "you", "need", "from", "me?"};
Nawet bezpośrednio można podzielić String
dosłownym:
String[] firstNames = "Mickey, Frank, Alicia, Tom".split(", ");
// Result is firstNames = {"Mickey", "Frank", "Alicia", "Tom"};
Ostrzeżenie : nie zapominaj, że parametr jest zawsze traktowany jako wyrażenie regularne.
"aaa.bbb".split("."); // This returns an empty array
W poprzednim przykładzie .
jest traktowany jako znak wieloznaczny wyrażenia regularnego pasujący do dowolnego znaku, a ponieważ każdy znak jest separatorem, wynikiem jest pusta tablica.
Dzielenie oparte na ograniczniku, który jest metaznakiem wyrażenia regularnego
Następujące znaki są uważane za specjalne (inaczej meta-znaki) w wyrażeniu regularnym
< > - = ! ( ) [ ] { } \ ^ $ | ? * + .
Aby podzielić ciąg znaków na podstawie jednego z powyższych ograniczników, musisz uciec z niego za pomocą \\
lub użyć Pattern.quote()
:
Za pomocą
Pattern.quote()
:String s = "a|b|c"; String regex = Pattern.quote("|"); String[] arr = s.split(regex);
Ucieczka od znaków specjalnych:
String s = "a|b|c"; String[] arr = s.split("\\|");
Podziel usuwa puste wartości
split(delimiter)
domyślnie usuwa końcowe ciągi znaków z tablicy wyników. Aby wyłączyć ten mechanizm, musimy użyć przeciążonej wersji split(delimiter, limit)
z limitem ustawionym na wartość ujemną, np.
String[] split = data.split("\\|", -1);
split(regex)
wewnętrznie zwraca wynik split(regex, 0)
.
Parametr limit kontroluje liczbę przypadków zastosowania wzorca, a zatem wpływa na długość wynikowej tablicy.
Jeśli granica n jest większa niż zero, wówczas wzór zostanie zastosowany co najwyżej n - 1 razy, długość tablicy nie będzie większa niż n , a ostatni wpis tablicy będzie zawierał wszystkie dane wejściowe poza ostatnim dopasowanym ogranicznikiem.
Jeśli n jest ujemne, wówczas wzór zostanie zastosowany tyle razy, ile to możliwe, a tablica może mieć dowolną długość.
Jeśli n wynosi zero, wówczas wzór zostanie zastosowany tyle razy, ile to możliwe, tablica może mieć dowolną długość, a końcowe ciągi znaków zostaną odrzucone.
Dzielenie za pomocą StringTokenizer
Oprócz metody split()
ciągi znaków można również podzielić za pomocą StringTokenizer
.
StringTokenizer
jest nawet bardziej restrykcyjny niż String.split()
, a także nieco trudniejszy w użyciu. Jest zasadniczo przeznaczony do wyciągania tokenów ograniczonych stałym zestawem znaków (podanych jako String
znaków). Każda postać będzie działać jako separator. Z powodu tego ograniczenia jest około dwa razy szybszy niż String.split()
.
Domyślnym zestawem znaków są puste spacje ( \t\n\r\f
). Poniższy przykład wydrukuje każde słowo osobno.
String str = "the lazy fox jumped over the brown fence";
StringTokenizer tokenizer = new StringTokenizer(str);
while (tokenizer.hasMoreTokens()) {
System.out.println(tokenizer.nextToken());
}
Spowoduje to wydrukowanie:
the
lazy
fox
jumped
over
the
brown
fence
Do separacji można używać różnych zestawów znaków.
String str = "jumped over";
// In this case character `u` and `e` will be used as delimiters
StringTokenizer tokenizer = new StringTokenizer(str, "ue");
while (tokenizer.hasMoreTokens()) {
System.out.println(tokenizer.nextToken());
}
Spowoduje to wydrukowanie:
j
mp
d ov
r
Łączenie ciągów z separatorem
Tablicę ciągów można połączyć za pomocą statycznej metody String.join()
:
String[] elements = { "foo", "bar", "foobar" };
String singleString = String.join(" + ", elements);
System.out.println(singleString); // Prints "foo + bar + foobar"
Podobnie istnieje metoda przeciążona String.join()
dla Iterable
s.
Aby mieć dokładną kontrolę nad łączeniem, możesz użyć klasy StringJoiner :
StringJoiner sj = new StringJoiner(", ", "[", "]");
// The last two arguments are optional,
// they define prefix and suffix for the result string
sj.add("foo");
sj.add("bar");
sj.add("foobar");
System.out.println(sj); // Prints "[foo, bar, foobar]"
Aby dołączyć do strumienia ciągów, możesz użyć łączącego kolektora :
Stream<String> stringStream = Stream.of("foo", "bar", "foobar");
String joined = stringStream.collect(Collectors.joining(", "));
System.out.println(joined); // Prints "foo, bar, foobar"
Istnieje również możliwość zdefiniowania przedrostka i przyrostka :
Stream<String> stringStream = Stream.of("foo", "bar", "foobar");
String joined = stringStream.collect(Collectors.joining(", ", "{", "}"));
System.out.println(joined); // Prints "{foo, bar, foobar}"
Cofanie ciągów znaków
Istnieje kilka sposobów odwrócenia łańcucha, aby zrobić go do tyłu.
StringBuilder / StringBuffer:
String code = "code"; System.out.println(code); StringBuilder sb = new StringBuilder(code); code = sb.reverse().toString(); System.out.println(code);
Tablica znaków:
String code = "code"; System.out.println(code); char[] array = code.toCharArray(); for (int index = 0, mirroredIndex = array.length - 1; index < mirroredIndex; index++, mirroredIndex--) { char temp = array[index]; array[index] = array[mirroredIndex]; array[mirroredIndex] = temp; } // print reversed System.out.println(new String(array));
Zliczanie wystąpień podłańcucha lub znaku w ciągu
countMatches
metoda z org.apache.commons.lang3.StringUtils jest zazwyczaj używany do zliczania wystąpień podciągu lub znaku w String
:
import org.apache.commons.lang3.StringUtils;
String text = "One fish, two fish, red fish, blue fish";
// count occurrences of a substring
String stringTarget = "fish";
int stringOccurrences = StringUtils.countMatches(text, stringTarget); // 4
// count occurrences of a char
char charTarget = ',';
int charOccurrences = StringUtils.countMatches(text, charTarget); // 3
W przeciwnym razie dla robi to samo ze standardowymi interfejsami API Java, możesz użyć wyrażeń regularnych:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
String text = "One fish, two fish, red fish, blue fish";
System.out.println(countStringInString("fish", text)); // prints 4
System.out.println(countStringInString(",", text)); // prints 3
public static int countStringInString(String search, String text) {
Pattern pattern = Pattern.compile(search);
Matcher matcher = pattern.matcher(text);
int stringOccurrences = 0;
while (matcher.find()) {
stringOccurrences++;
}
return stringOccurrences;
}
Łańcuch konkatenacji i StringBuilders
Łączenie łańcuchów można wykonać za pomocą operatora +
. Na przykład:
String s1 = "a";
String s2 = "b";
String s3 = "c";
String s = s1 + s2 + s3; // abc
Zwykle implementacja kompilatora wykona powyższą konkatenację przy użyciu metod obejmujących StringBuilder
pod maską. Po skompilowaniu kod wyglądałby podobnie do poniższego:
StringBuilder sb = new StringBuilder("a");
String s = sb.append("b").append("c").toString();
StringBuilder
ma kilka przeciążonych metod dołączania różnych typów, na przykład w celu dodania int
zamiast String
. Na przykład implementacja może konwertować:
String s1 = "a";
String s2 = "b";
String s = s1 + s2 + 2; // ab2
do następujących:
StringBuilder sb = new StringBuilder("a");
String s = sb.append("b").append(2).toString();
Powyższe przykłady ilustrują prostą operację konkatenacji, która jest skutecznie wykonywana w jednym miejscu w kodzie. Łączenie obejmuje pojedyncze wystąpienie StringBuilder
. W niektórych przypadkach konkatenacja jest przeprowadzana w sposób skumulowany, na przykład w pętli:
String result = "";
for(int i = 0; i < array.length; i++) {
result += extractElement(array[i]);
}
return result;
W takich przypadkach optymalizacja kompilatora zwykle nie jest stosowana, a każda iteracja utworzy nowy obiekt StringBuilder
. Można to zoptymalizować, jawnie przekształcając kod w celu użycia jednego StringBuilder
:
StringBuilder result = new StringBuilder();
for(int i = 0; i < array.length; i++) {
result.append(extractElement(array[i]));
}
return result.toString();
StringBuilder
zostanie zainicjowany pustą przestrzenią składającą się tylko z 16 znaków. Jeśli wiesz z góry, że będziesz budował większe ciągi, może być korzystne zainicjowanie go z wystarczającą wielkością z wyprzedzeniem, aby nie trzeba zmieniać rozmiaru bufora wewnętrznego:
StringBuilder buf = new StringBuilder(30); // Default is 16 characters
buf.append("0123456789");
buf.append("0123456789"); // Would cause a reallocation of the internal buffer otherwise
String result = buf.toString(); // Produces a 20-chars copy of the string
Jeśli produkujesz wiele ciągów, zaleca się ponowne użycie StringBuilder
:
StringBuilder buf = new StringBuilder(100);
for (int i = 0; i < 100; i++) {
buf.setLength(0); // Empty buffer
buf.append("This is line ").append(i).append('\n');
outputfile.write(buf.toString());
}
Jeśli (i tylko jeśli) wiele wątków pisze do tego samego bufora, użyj StringBuffer , który jest synchronized
wersją StringBuilder
. Ale ponieważ zwykle tylko jeden wątek zapisuje w buforze, zwykle jest szybsze użycie StringBuilder
bez synchronizacji.
Za pomocą metody concat ():
String string1 = "Hello ";
String string2 = "world";
String string3 = string1.concat(string2); // "Hello world"
Zwraca nowy ciąg, który jest ciągiem 1 z ciągiem2 dodanym do niego na końcu. Możesz także użyć metody concat () z literałami ciągów, jak w:
"My name is ".concat("Buyya");
Wymiana części ciągów
Dwa sposoby zamiany: przez wyrażenie regularne lub dopasowanie ścisłe.
Uwaga: oryginalny obiekt String pozostanie niezmieniony, zwracana wartość zawiera zmieniony String.
Dokładne dopasowanie
Zamień pojedynczy znak na inny pojedynczy znak:
String replace(char oldChar, char newChar)
Zwraca nowy ciąg wynikający z zamiany wszystkich wystąpień oldChar w tym ciągu na newChar.
String s = "popcorn";
System.out.println(s.replace('p','W'));
Wynik:
WoWcorn
Zamień sekwencję znaków na inną sekwencję znaków:
String replace(CharSequence target, CharSequence replacement)
Zastępuje każdy podciąg tego ciągu, który jest zgodny z dosłowną sekwencją docelową z określoną dosłowną sekwencją zastępczą.
String s = "metal petal et al.";
System.out.println(s.replace("etal","etallica"));
Wynik:
metallica petallica et al.
Regex
Uwaga : grupowanie używa znaku $
celu odniesienia do grup, np. $1
.
Zamień wszystkie dopasowania:
String replaceAll(String regex, String replacement)
Zastępuje każdy podciąg tego łańcucha, który pasuje do podanego wyrażenia regularnego z podanym zamiennikiem.
String s = "spiral metal petal et al.";
System.out.println(s.replaceAll("(\\w*etal)","$1lica"));
Wynik:
spiral metallica petallica et al.
Zamień tylko pierwsze dopasowanie:
String replaceFirst(String regex, String replacement)
Zastępuje pierwszy podciąg tego łańcucha, który pasuje do podanego wyrażenia regularnego z podanym zamiennikiem
String s = "spiral metal petal et al.";
System.out.println(s.replaceAll("(\\w*etal)","$1lica"));
Wynik:
spiral metallica petal et al.
Usuń białe znaki z początku i końca łańcucha
Metoda trim()
zwraca nowy ciąg znaków z usuniętymi początkowymi i końcowymi spacjami.
String s = new String(" Hello World!! ");
String t = s.trim(); // t = "Hello World!!"
Jeśli trim
ciąg znaków, który nie ma żadnych białych znaków do usunięcia, zostanie zwrócona ta sama instancja ciągu.
Zauważ, że metoda trim()
ma własne pojęcie białych znaków , które różni się od pojęcia używanego przez metodę Character.isWhitespace()
:
Wszystkie znaki kontrolne ASCII o kodach od
U+0000
doU+0020
są uważane za białe znaki i są usuwane przeztrim()
. Obejmuje toU+0009 'CHARACTER TABULATION'
U+0020 'SPACE'
,U+0009 'CHARACTER TABULATION'
,U+000A 'LINE FEED'
iU+000D 'CARRIAGE RETURN'
, ale także znaki takie jakU+0007 'BELL'
.U+00A0 'NO-BREAK SPACE'
znaki Unicode, takie jakU+00A0 'NO-BREAK SPACE'
lubU+2003 'EM SPACE'
nie są rozpoznawane przeztrim()
.
Pula ciągów i pamięć sterty
Podobnie jak wiele obiektów Java, wszystkie wystąpienia String
są tworzone na stercie, nawet literały. Kiedy JVM znajdzie String
dosłowne, że nie ma odpowiednika odniesienie w stercie, JVM tworzy odpowiedni String
instancji na stercie a także przechowuje referencję do nowo utworzonego String
przykład w basenie String. Wszelkie inne odniesienia do tego samego literału String
są zastępowane przez wcześniej utworzoną instancję String
w stercie.
Spójrzmy na następujący przykład:
class Strings
{
public static void main (String[] args)
{
String a = "alpha";
String b = "alpha";
String c = new String("alpha");
//All three strings are equivalent
System.out.println(a.equals(b) && b.equals(c));
//Although only a and b reference the same heap object
System.out.println(a == b);
System.out.println(a != c);
System.out.println(b != c);
}
}
Wynikiem powyższego jest:
true
true
true
true
Kiedy używamy podwójnych cudzysłowów do utworzenia ciągu, najpierw szuka ciągu o tej samej wartości w puli ciągów, jeśli zostanie znaleziony, po prostu zwróci odwołanie, w przeciwnym razie utworzy nowy ciąg w puli, a następnie zwróci odwołanie.
Jednak używając nowego operatora, zmuszamy klasę String do utworzenia nowego obiektu String w przestrzeni sterty. Możemy użyć metody intern (), aby umieścić ją w puli lub odwołać się do innego obiektu String z puli stringów o tej samej wartości.
Sama pula ciągów jest również tworzona na stercie.
Przed Javą 7 literały String
były przechowywane w puli stałej środowiska wykonawczego w obszarze metod PermGen
, który miał stały rozmiar.
Pula ciągów również rezydowała w PermGen
.
W JDK 7 internowane ciągi nie są już alokowane w stałej generacji sterty Java, ale zamiast tego są alokowane w głównej części sterty Java (zwanej młodą i starą generacją), wraz z innymi obiektami tworzonymi przez aplikację . Ta zmiana spowoduje, że więcej danych będzie znajdować się w głównej sterty Java, a mniej danych w generacji stałej, a zatem może wymagać dostosowania wielkości sterty. Większość aplikacji zobaczy tylko stosunkowo niewielkie różnice w stosie ze względu na tę zmianę, ale większe aplikacje, które ładują wiele klas lub intensywnie korzystają z metody
String.intern()
, zauważą bardziej znaczące różnice.
Przełącznik bez rozróżniania wielkości liter
sam switch
nie może zostać sparametryzowany, aby rozróżniał małe i wielkie litery, ale jeśli jest to absolutnie wymagane, może zachowywać się niewrażliwy na łańcuch wejściowy za pomocą toLowerCase()
lub toUpperCase
:
switch (myString.toLowerCase()) {
case "case1" :
...
break;
case "case2" :
...
break;
}
Strzec się
-
Locale
mogą wpływać na zmianę przypadków ! - Należy uważać, aby na etykietach nie było wielkich liter - nigdy nie zostaną one wykonane!