Szukaj…


Wprowadzenie

Ciągi ( java.lang.String ) to fragmenty tekstu przechowywane w twoim programie. Ciągi nieprymitywnym 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.

Java SE 7

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.

Java SE 7

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.
}

Demo na żywo

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.
}

Demo na żywo

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

Demo na żywo


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

Java SE 7

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

Demo na żywo

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 .

Java SE 7

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 metody String.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:

  1. Zadeklaruj ciąg.
  2. Wpisz ciąg.
  3. Przekształć ciąg w tablicę znaków.
  4. Wpisz znak, który chcesz przeszukać.
  5. Wyszukaj znak w tablicy znaków.
  6. 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.
  7. Zmień oryginalny znak z tablicy znaków.
  8. 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

Demo na żywo w Ideone


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

Demo na żywo w Ideone

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

Demo na żywo w Ideone

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

Demo na żywo w Ideone

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.

Java SE 7

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.

Java SE 7

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")
Java SE 7

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

Demo na żywo w Ideone

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

Demo na żywo w Ideone

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

Java SE 8

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.

  1. StringBuilder / StringBuffer:

     String code = "code";
     System.out.println(code);
    
     StringBuilder sb = new StringBuilder(code); 
     code = sb.reverse().toString();
    
     System.out.println(code);
    
  2. 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 do U+0020 są uważane za białe znaki i są usuwane przez trim() . Obejmuje to U+0009 'CHARACTER TABULATION' U+0020 'SPACE' , U+0009 'CHARACTER TABULATION' , U+000A 'LINE FEED' i U+000D 'CARRIAGE RETURN' , ale także znaki takie jak U+0007 'BELL' .

  • U+00A0 'NO-BREAK SPACE' znaki Unicode, takie jak U+00A0 'NO-BREAK SPACE' lub U+2003 'EM SPACE' nie są rozpoznawane przez trim() .

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

Schemat puli Java Heap i String 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.

Java SE 7

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 .

Java SE 7

RFC: 6962931

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

Java SE 7

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!


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow