Поиск…


Вступление

Строки ( java.lang.String ) - это фрагменты текста, хранящиеся в вашей программе. Строки не являются примитивным типом данных на Java , однако они очень распространены в программах Java.

В Java строки являются неизменными, что означает, что они не могут быть изменены. (Нажмите здесь, чтобы получить более подробное объяснение неизменности.)

замечания

Поскольку строки Java неизменяемы , все методы, которые управляют String , возвращают новый объект String . Они не меняют исходную String . Это включает в себя методы подстроки и замещения, которые программисты на C и C ++ ожидали бы изменить целевой объект String .


Используйте StringBuilder вместо String если вы хотите объединить более двух объектов String , значения которых не могут быть определены во время компиляции. Этот метод более эффективен, чем создание новых объектов String и их объединение, поскольку StringBuilder изменен.

StringBuffer также может использоваться для конкатенации объектов String . Однако этот класс менее эффективен, потому что он предназначен для обеспечения потокобезопасности и получает мьютекс перед каждой операцией. Поскольку вы почти никогда не нуждаетесь в защите потоков при конкатенации строк, лучше всего использовать StringBuilder .

Если вы можете выразить конкатенацию строк как одно выражение, тогда лучше использовать оператор + . Компилятор Java преобразует выражение, содержащее + конкатенации, в эффективную последовательность операций, используя либо String.concat(...) либо StringBuilder . Совет по использованию StringBuilder явно применяется только тогда, когда конкатенация включает в себя несколько выражений.


Не храните конфиденциальную информацию в строках. Если кто-то может получить дамп памяти вашего запущенного приложения, тогда они смогут найти все существующие объекты String и прочитать их содержимое. Сюда входят объекты String , недоступные и ожидающие сбора мусора. Если это вызывает беспокойство, вам нужно будет стереть конфиденциальные строковые данные, как только вы закончите с этим. Вы не можете сделать это с объектами String поскольку они неизменяемы. Поэтому рекомендуется использовать объекты char[] для хранения конфиденциальных символьных данных и уничтожить их (например, перезаписать их символами '\000' ), когда вы закончите.


Все экземпляры String создаются в куче, даже экземпляры, соответствующие строковым литералам. Особенность струнных литералов в том, что JVM гарантирует, что все литералы, которые являются равными (т.е. состоят из одних и тех же символов), представлены одним объектом String (это поведение указано в JLS). Это реализовано загрузчиками классов JVM. Когда загрузчик классов загружает класс, он сканирует строковые литералы, которые используются в определении класса, каждый раз, когда он видит один, он проверяет, есть ли уже запись в пуле строк для этого литерала (используя литерал как ключ) , Если уже есть запись для литерала, используется ссылка на экземпляр String хранящийся как пара для этого литерала. В противном случае создается новый экземпляр String и ссылка на экземпляр хранится для литерала (используется как ключ) в пуле строк. (Также см. Интернирование строк ).

Пул строк хранится в куче Java и подчиняется обычной сборке мусора.

Java SE 7

В версиях Java до Java 7 пул строк состоялся в особой части кучи, известной как «PermGen». Эта часть собиралась лишь иногда.

Java SE 7

В Java 7 пул строк был удален из «PermGen».

Обратите внимание, что строковые литералы неявно достижимы из любого метода, который их использует. Это означает, что соответствующие объекты String могут быть собраны только при сборке мусора, если сам код является сборкой мусора.


До Java 8 объекты String реализованы как массив символов UTF-16 (2 байта на символ). В Java 9 предлагается реализовать String как массив байтов с полем флага кодирования, чтобы отметить, что строка кодируется как байты (LATIN-1) или символы (UTF-16).

Сравнение строк

Для сравнения строк равенства, следует использовать строковый объект в equals или equalsIgnoreCase методы.

Например, следующий фрагмент будет определять, равны ли два экземпляра String для всех символов:

String firstString = "Test123";
String secondString = "Test" + 123;

if (firstString.equals(secondString)) {
   // Both Strings have the same content.
}

Демо-версия

Этот пример будет сравнивать их, независимо от их случая:

String firstString = "Test123";
String secondString = "TEST123";

if (firstString.equalsIgnoreCase(secondString)) {
    // Both Strings are equal, ignoring the case of the individual characters.
}

Демо-версия

Обратите внимание: equalsIgnoreCase не позволяет указать Locale . Например, если вы сравниваете два слова "Taki" и "TAKI" на английском языке, они равны; однако на турецком языке они разные (на турецком, нижний регистр I - ı ). Для таких случаев преобразование обеих строк в нижний регистр (или в верхний регистр) с помощью Locale а затем сравнение с 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

Демо-версия


Не используйте оператор == для сравнения строк

Если вы не можете гарантировать, что все строки были интернированы (см. Ниже), вы не должны использовать операторы == или != Для сравнения строк. Эти операторы фактически проверяют ссылки, и поскольку несколько объектов String могут представлять одну и ту же строку, это может привести к неправильному ответу.

Вместо этого используйте метод String.equals(Object) , который будет сравнивать объекты String на основе их значений. Подробное объяснение см. В Pitfall: использование == для сравнения строк .


Сравнение строк в инструкции switch

Java SE 7

Начиная с Java 1.7, можно сравнить переменную String с литералами в инструкции switch . Убедитесь, что String не имеет значения null, иначе он всегда будет генерировать NullPointerException . Значения сравниваются с использованием String.equals , т.е. чувствительны к регистру.

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

Демо-версия

Сравнение строк с постоянными значениями

При сравнении значения String с константой вы можете поместить постоянное значение в левой части equals чтобы убедиться, что вы не получите NullPointerException если другая String равна null .

"baz".equals(foo)

В то время как foo.equals("baz") выкинет foo.equals("baz") NullPointerException если foo имеет значение null , "baz".equals(foo) будет оцениваться как false .

Java SE 7

Более читаемой альтернативой является использование Objects.equals() , которая выполняет нулевую проверку обоих параметров: Objects.equals(foo, "baz") .

(Примечание: Это спорно , как к тому, что лучше избегать NullPointerExceptions в целом, или пусть произойдет , а затем устранить причину, см здесь и здесь , конечно, назвав стратегию избегания «лучшая практика» не является оправданной.) .

Строковые упорядочения

Класс String реализует Comparable<String> с методом String.compareTo (как описано в начале этого примера). Это делает естественным упорядочение String объектов с учетом регистра. Класс String предоставляет константу Comparator<String> называемую CASE_INSENSITIVE_ORDER подходящую для сортировки без CASE_INSENSITIVE_ORDER регистра.

Сравнение с интернированными строками

Спецификация языка Java ( JLS 3.10.6 ) гласит следующее:

Более того, строковый литерал всегда ссылается на один и тот же экземпляр класса String . Это связано с тем, что строковые литералы, или, в более общем смысле, строки, являющиеся значениями константных выражений, интернированы, чтобы обмениваться уникальными экземплярами, используя метод String.intern ".

Это означает, что безопасно сравнивать ссылки на два строковых литерала, используя == . Более того, то же самое верно для ссылок на объекты String , которые были созданы с использованием String.intern() .

Например:

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

За кулисами механизм интернирования поддерживает хеш-таблицу, содержащую все интернированные строки, которые все еще доступны . Когда вы вызываете intern() в String , метод ищет объект в хеш-таблице:

  • Если строка найдена, то это значение возвращается как интернированная строка.
  • В противном случае копия строки добавляется в хэш-таблицу, и эта строка возвращается как интернированная строка.

Можно использовать интернирование, чтобы строки могли сравниваться с помощью == . Однако есть серьезные проблемы с этим; см. Pitfall. Интернированные строки, чтобы вы могли использовать ==, - это плохая идея для деталей. Это не рекомендуется в большинстве случаев.

Изменение случая символов внутри строки

Тип String предоставляет два метода преобразования строк между верхним регистром и нижним регистром:

  • toUpperCase для преобразования всех символов в верхний регистр
  • toLowerCase для преобразования всех символов в нижний регистр

Эти методы возвращают преобразованные строки в виде новых экземпляров String : исходные объекты String не изменяются, поскольку String неизменна в Java. См. Это больше для неизменяемости: неизменность строк в Java

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"

Этими методами не затрагиваются неалфавитные символы, такие как цифры и знаки препинания. Обратите внимание, что эти методы также могут неправильно обрабатывать определенные символы Unicode при определенных условиях.


Примечание . Эти методы чувствительны к локали и могут давать неожиданные результаты, если они используются в строках, которые предназначены для интерпретации независимо от языка. Примерами являются идентификаторы языка программирования, ключи протокола и теги HTML .

Например, "TITLE".toLowerCase() в турецком языке возвращает « tıtle », где ı (\u0131) является ı (\u0131) LATIN SMALL LETTER DOTLESS I. Чтобы получить правильные результаты для нечувствительных к регистру строк, перейдите в Locale.ROOT как параметр к соответствующему методу преобразования случая (например, toLowerCase(Locale.ROOT) или toUpperCase(Locale.ROOT) ).

Хотя использование Locale.ENGLISH также верно для большинства случаев, инвариантным языком является Locale.ROOT .

Подробный список символов Юникода, требующих специальной оболочки, можно найти на веб-сайте Консорциума Юникода .

Изменение случая определенного символа в строке ASCII:

Чтобы изменить случай конкретного символа строки ASCII, можно использовать следующий алгоритм:

шаги:

  1. Объявите строку.
  2. Введите строку.
  3. Преобразуйте строку в массив символов.
  4. Введите символ, который нужно искать.
  5. Поиск символа в массиве символов.
  6. Если найден, проверьте, имеет ли символ строчный или верхний регистр.
    • Если в верхнем регистре добавьте 32 к коду ASCII символа.
    • Если в нижнем регистре вычесть 32 из ASCII-кода символа.
  7. Измените исходный символ из массива символов.
  8. Преобразуйте массив символов в строку.

Вуаля, Дело о персонаже изменено.

Примером кода для алгоритма является:

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

Поиск строки в другой строке

Чтобы проверить, содержится ли конкретная строка a в String.contains() b или нет, мы можем использовать метод String.contains() со следующим синтаксисом:

b.contains(a); // Return true if a is contained in b, false otherwise

Метод String.contains() может использоваться для проверки наличия CharSequence в String. Метод ищет струнный a в строке b в регистрозависимой образом.

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

Живая демонстрация на Ideone


Чтобы найти точную позицию, в которой строка начинается с другой строки, используйте 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

Живая демонстрация на Ideone

Метод String.indexOf() возвращает первый индекс char или String в другой String . Метод возвращает -1 если он не найден.

Примечание . Метод String.indexOf() чувствителен к регистру.

Пример поиска, игнорирующий случай:

String str1 = "Hello World";
String str2 = "wOr";
str1.indexOf(str2);                               // -1
str1.toLowerCase().contains(str2.toLowerCase());  // true
str1.toLowerCase().indexOf(str2.toLowerCase());   // 6

Живая демонстрация на Ideone

Получение длины строки

Чтобы получить длину объекта String , вызовите на нем метод length() . Длина равна количеству кодовых единиц UTF-16 (символов) в строке.

String str = "Hello, World!";
System.out.println(str.length()); // Prints out 13

Живая демонстрация на Ideone

char в строке является значение UTF-16. Кодовые страницы Unicode, значения которых составляют ≥ 0x1000 (например, большинство emojis), используют две позиции char. Чтобы подсчитать количество кодовых точек Unicode в String, независимо от того, соответствует ли каждый код в значении char UTF-16, вы можете использовать метод codePointCount :

int length = str.codePointCount(0, str.length());

Вы также можете использовать Stream of codepoints, начиная с Java 8:

int length = str.codePoints().count();

Подстроки

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

Подстроки могут также применяться к фрагменту и добавлять / заменять символ в его исходную строку. Например, вы столкнулись с китайской датой, содержащей китайские иероглифы, но хотите сохранить ее в виде строкового формата даты.

String datestring = "2015年11月17日"
datestring = datestring.substring(0, 4) + "-" + datestring.substring(5,7) + "-" + datestring.substring(8,10);
//Result will be 2015-11-17

Метод подстроки извлекает часть String . При наличии одного параметра параметр является началом, а кусок продолжается до конца String . При задании двух параметров первым параметром является начальный символ, а второй параметр - индекс символа сразу после конца (символ в индексе не включен). Легкий способ проверки заключается в том, что вычитание первого параметра из второго должно приводить к ожидаемой длине строки.

Java SE 7

В версиях JDK <7u6 метод substring создает экземпляр String который имеет один и тот же базовый char[] в качестве исходной String и имеет внутренние поля offset и count заданные для начала и длины результата. Такое совместное использование может привести к утечкам памяти, что может быть предотвращено вызовом new String(s.substring(...)) для принудительного создания копии, после чего char[] может быть собран в мусор.

Java SE 7

Из JDK 7u6 метод substring всегда копирует весь базовый массив char[] , делая сложность линейной по сравнению с предыдущей константой, но гарантируя отсутствие утечек памяти в одно и то же время.

Получение n-го символа в строке

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"

Чтобы получить n-й символ в строке, просто вызовите charAt(n) в String , где n - это индекс символа, который вы хотите получить

ПРИМЕЧАНИЕ: индекс n начинается с 0 , поэтому первый элемент находится при n = 0.

Независимый от платформы новый разделитель строк

Поскольку новый разделитель строк варьируется от платформы к платформе (например, \n в Unix-подобных системах или \r\n в Windows), часто необходимо иметь независимый от платформы способ доступа к нему. В Java он может быть получен из системного свойства:

System.getProperty("line.separator")
Java SE 7

Поскольку новый разделитель строк так часто необходим, из Java 7 по методу быстрого доступа, возвращающему точно такой же результат, как и код выше, доступен:

System.lineSeparator()

Примечание . Поскольку маловероятно, что новый разделитель строк изменяется во время выполнения программы, рекомендуется хранить его в статической конечной переменной, а не извлекать его из системного свойства каждый раз, когда это необходимо.

При использовании String.format используйте %n вместо \n или '\ r \ n' для вывода независимого от платформы нового разделителя строк.

System.out.println(String.format('line 1: %s.%nline 2: %s%n', lines[0],lines[1]));

Добавление метода toString () для настраиваемых объектов

Предположим, вы определили следующий класс Person :

public class Person {

    String name;
    int age;
    
    public Person (int age, String name) {
        this.age = age;
        this.name = name;
    }
}

Если вы создаете экземпляр нового объекта Person :

Person person = new Person(25, "John");

и позже в вашем коде вы используете следующий оператор для печати объекта:

System.out.println(person.toString());

Живая демонстрация на Ideone

вы получите результат, похожий на следующий:

Person@7ab89d

Это результат реализации метода toString() определенного в классе Object , суперклассе Person . Документация объекта Object.toString() гласит:

Метод toString для класса Object возвращает строку, состоящую из имени класса, объектом которого является экземпляр, символа at-sign `@ 'и шестизначного шестнадцатеричного представления хеш-кода объекта. Другими словами, этот метод возвращает строку, равную значению:

getClass().getName() + '@' + Integer.toHexString(hashCode())

Таким образом, для значимого вывода вам придется переопределить метод toString() :

@Override
public String toString() {
    return "My name is " + this.name + " and my age is " + this.age;
}

Теперь выход будет:

My name is John and my age is 25

Вы также можете написать

System.out.println(person);

Живая демонстрация на Ideone

На самом деле println() неявно вызывает метод toString для объекта.

Разделение строк

Вы можете разделить String на конкретный разделительный символ или регулярное выражение , вы можете использовать метод String.split() который имеет следующую подпись:

public String[] split(String regex)

Обратите внимание, что разделительный символ или регулярное выражение удаляется из результирующего массива String.

Пример с использованием разделительного символа:

String lineFromCsvFile = "Mickey;Bolton;12345;121216";
String[] dataCells = lineFromCsvFile.split(";");
// Result is dataCells = { "Mickey", "Bolton", "12345", "121216"};

Пример с использованием регулярного выражения:

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

Вы даже можете разделить String литерал:

String[] firstNames = "Mickey, Frank, Alicia, Tom".split(", ");
// Result is firstNames = {"Mickey", "Frank", "Alicia", "Tom"};

Предупреждение . Не забывайте, что параметр всегда рассматривается как регулярное выражение.

"aaa.bbb".split("."); // This returns an empty array

В предыдущем примере . рассматривается как подстановочный знак регулярного выражения, который соответствует любому символу, и поскольку каждый символ является разделителем, результатом является пустой массив.


Разделение на основе разделителя, который является метасимволом регулярного выражения

Следующие символы считаются специальными (иначе метасимволами) в регулярном выражении

  < > - = ! ( ) [ ] { } \ ^ $ | ? * + . 

Чтобы разбить строку на основе одного из указанных разделителей, вам нужно либо сбежать с помощью \\ либо использовать Pattern.quote() :

  • Использование Pattern.quote() :

     String s = "a|b|c";
     String regex = Pattern.quote("|");
     String[] arr = s.split(regex);
    
  • Выход из специальных символов:

     String s = "a|b|c";
     String[] arr = s.split("\\|");
    

Сплит удаляет пустые значения

split(delimiter) по умолчанию удаляет конечные пустые строки из массива результатов. Чтобы отключить этот механизм, нам нужно использовать перегруженную версию split(delimiter, limit) с ограничением, установленным на отрицательное значение, например

String[] split = data.split("\\|", -1);

split(regex) внутренне возвращает результат split(regex, 0) .

Предельный параметр управляет количеством применений шаблона и, следовательно, влияет на длину результирующего массива.
Если предел n больше нуля, шаблон будет применен не более n - 1 раз, длина массива будет не больше n , а последняя запись массива будет содержать все входные данные за пределами последнего сопоставленного разделителя.
Если n отрицательно, то шаблон будет применяться столько раз, сколько возможно, и массив может иметь любую длину.
Если n равно нулю, шаблон будет применяться столько раз, сколько возможно, массив может иметь любую длину, а конечные пустые строки будут отброшены.


Разделение с помощью StringTokenizer

Помимо метода split() Строки также можно разделить с помощью StringTokenizer .

StringTokenizer является еще более ограничивающим, чем String.split() , а также немного сложнее в использовании. Он по существу предназначен для вытаскивания токенов, ограниченных фиксированным набором символов (заданных как String ). Каждый символ будет действовать как разделитель. Из-за этого ограничения он примерно в два раза быстрее, чем String.split() .

Набор символов по умолчанию - это пустые пространства ( \t\n\r\f ). Следующий пример будет печатать каждое слово отдельно.

String str = "the lazy fox jumped over the brown fence";
StringTokenizer tokenizer = new StringTokenizer(str);
while (tokenizer.hasMoreTokens()) {
    System.out.println(tokenizer.nextToken());
}

Это напечатает:

the
lazy 
fox 
jumped 
over 
the 
brown 
fence

Вы можете использовать разные наборы символов для разделения.

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

Это напечатает:

j
mp 
d ov
r

Объединение строк с разделителем

Java SE 8

Массив строк можно объединить с помощью статического метода String.join() :

String[] elements = { "foo", "bar", "foobar" };
String singleString = String.join(" + ", elements);

System.out.println(singleString); // Prints "foo + bar + foobar"     

Точно так же существует перегруженный String.join() для Iterable s.


Чтобы иметь мелкозернистый контроль над присоединением, вы можете использовать класс 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]"

Чтобы присоединиться к потоку строк, вы можете использовать сборщик :

Stream<String> stringStream = Stream.of("foo", "bar", "foobar");
String joined = stringStream.collect(Collectors.joining(", "));
System.out.println(joined); // Prints "foo, bar, foobar"

Здесь также можно указать префикс и суффикс :

Stream<String> stringStream = Stream.of("foo", "bar", "foobar");
String joined = stringStream.collect(Collectors.joining(", ", "{", "}"));
System.out.println(joined); // Prints "{foo, bar, foobar}"

Реверсивные струны

Есть несколько способов изменить строку, чтобы сделать ее обратно.

  1. StringBuilder / StringBuffer:

     String code = "code";
     System.out.println(code);
    
     StringBuilder sb = new StringBuilder(code); 
     code = sb.reverse().toString();
    
     System.out.println(code);
    
  2. Char array:

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

Подсчет вхождений подстроки или символа в строке

Метод countMatches из org.apache.commons.lang3.StringUtils обычно используется для подсчета вхождения подстроки или символа в 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

В противном случае для того же, что и для стандартных Java API, вы можете использовать регулярные выражения:

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

Конкатенация строк и StringBuilders

Конкатенацию строк можно выполнить с помощью оператора + . Например:

String s1 = "a";
String s2 = "b";
String s3 = "c";
String s = s1 + s2 + s3; // abc

Обычно реализация компилятора будет выполнять вышеупомянутую конкатенацию с использованием методов, связанных с StringBuilder под капотом. При компиляции код будет выглядеть примерно так:

StringBuilder sb = new StringBuilder("a");
String s = sb.append("b").append("c").toString();

StringBuilder есть несколько перегруженных методов для добавления разных типов, например, для добавления int вместо String . Например, реализация может конвертировать:

String s1 = "a";
String s2 = "b";    
String s = s1 + s2 + 2; // ab2

к следующему:

StringBuilder sb = new StringBuilder("a");
String s = sb.append("b").append(2).toString();

Вышеприведенные примеры иллюстрируют простую операцию конкатенации, которая эффективно выполняется в одном месте в коде. Конкатенация включает в себя один экземпляр StringBuilder . В некоторых случаях конкатенация осуществляется кумулятивным образом, например, в цикле:

String result = "";
for(int i = 0; i < array.length; i++) {
    result += extractElement(array[i]);
}
return result;

В таких случаях оптимизация компилятора обычно не применяется, и каждая итерация создаст новый объект StringBuilder . Это можно оптимизировать, явно преобразуя код для использования одного StringBuilder :

StringBuilder result = new StringBuilder();
for(int i = 0; i < array.length; i++) {
    result.append(extractElement(array[i]));
}
return result.toString();

StringBuilder будет инициализирован пустым пространством всего 16 символов. Если вы заранее знаете, что будете строить большие строки, может быть полезно инициализировать его с достаточным размером заранее, так что внутренний буфер не нужно изменять:

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

Если вы производите много строк, рекомендуется повторно использовать StringBuilder s:

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

Если (и только если) несколько потоков записываются в один и тот же буфер, используйте StringBuffer , который является synchronized версией StringBuilder . Но поскольку, как правило, только один поток записывает в буфер, обычно быстрее использовать StringBuilder без синхронизации.

Использование метода concat ():

String string1 = "Hello ";
String string2 = "world";
String string3 = string1.concat(string2);  // "Hello world"

Это возвращает новую строку, которая является string1 с добавлением string2 к ней в конце. Вы также можете использовать метод concat () со строковыми литералами, как в:

"My name is ".concat("Buyya");

Замена частей строк

Два способа заменить: регулярным выражением или точным совпадением.

Примечание: исходный объект String не изменится, возвращаемое значение содержит измененную строку.

Полное совпадение

Замените одиночный символ другим символом:

String replace(char oldChar, char newChar) 

Возвращает новую строку в результате замены всех вхождений oldChar в этой строке с помощью newChar.

String s = "popcorn";
System.out.println(s.replace('p','W'));

Результат:

WoWcorn

Замените последовательность символов другой последовательностью символов:

String replace(CharSequence target, CharSequence replacement) 

Заменяет каждую подстроку этой строки, которая соответствует буквенной целевой последовательности с указанной последовательностью замены литерала.

String s = "metal petal et al.";
System.out.println(s.replace("etal","etallica"));

Результат:

metallica petallica et al.

Regex

Примечание : группировка использует символ $ для ссылки на группы, например $1 .

Заменить все совпадения:

String replaceAll(String regex, String replacement) 

Заменяет каждую подстроку этой строки, которая соответствует данному регулярному выражению с указанной заменой.

String s = "spiral metal petal et al.";
System.out.println(s.replaceAll("(\\w*etal)","$1lica"));

Результат:

spiral metallica petallica et al.

Заменить только первое совпадение:

String replaceFirst(String regex, String replacement) 

Заменяет первую подстроку этой строки, которая соответствует данному регулярному выражению с указанной заменой

String s = "spiral metal petal et al.";
System.out.println(s.replaceAll("(\\w*etal)","$1lica"));

Результат:

spiral metallica petal et al.

Удаление пробелов с начала и конца строки

Метод trim() возвращает новую строку с удаленным пробелом и конечным пробелом.

String s = new String("   Hello World!!  ");
String t = s.trim();  // t = "Hello World!!"

Если вы trim строку, которая не имеет пробелов для удаления, вам будет возвращен тот же экземпляр String.

Обратите внимание, что метод trim() имеет свое собственное понятие пробела , которое отличается от понятия, используемого методом Character.isWhitespace() :

  • Все управляющие символы ASCII с кодами U+0000 до U+0020 считаются простыми и удаляются с помощью trim() . Это включает в себя U+0020 'SPACE' , U+0009 'CHARACTER TABULATION' , U+000A 'LINE FEED' и U+000D 'CARRIAGE RETURN' , но также символы, такие как U+0007 'BELL' .

  • U+00A0 'NO-BREAK SPACE' Unicode, такие как U+00A0 'NO-BREAK SPACE' или U+2003 'EM SPACE' , не распознаются trim() .

Струнный пул и хранилище кучи

Как и многие объекты Java, все экземпляры String создаются в куче, даже в литералах. Когда JVM находит String буквальные , что не имеют аналогов ссылки в куче, виртуальная машина создает соответствующий String экземпляр в куче , и он также сохраняет ссылку на вновь созданном String например в строке пуле. Любые другие ссылки на один и тот же String литерал заменяются ранее созданным экземпляром String в куче.

Давайте посмотрим на следующий пример:

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

Вышеуказанный результат:

true
true
true
true

Схема Java-кучи и пула строк Когда мы используем двойные кавычки для создания String, сначала он ищет String с одинаковым значением в пуле String, если он просто возвращает ссылку else, он создает новую строку в пуле и затем возвращает ссылку.

Однако, используя новый оператор, мы вынуждаем класс String создавать новый объект String в кучном пространстве. Мы можем использовать метод intern (), чтобы поместить его в пул или обратиться к другому объекту String из пула строк, имеющего такое же значение.

Сам пул строк также создается в куче.

Java SE 7

До Java 7 String литералы сохранялись в пуле постоянной среды выполнения в области методов PermGen , у которых был фиксированный размер.

Пул строк также находился в PermGen .

Java SE 7

RFC: 6962931

В JDK 7 интернированные строки больше не выделяются в постоянном поколении кучи Java, а вместо этого выделяются в основной части кучи Java (так называемые молодые и старые поколения) вместе с другими объектами, созданными приложением , Это изменение приведет к большему количеству данных, находящихся в основной куче Java, и меньше данных в постоянном поколении, и, следовательно, может потребоваться корректировка размеров кучи. Из-за этого большинства приложений будут наблюдаться лишь относительно небольшие различия в использовании кучи, но более крупные приложения, загружающие многие классы или интенсивно использующие метод String.intern() будут видеть более значительные различия.

Нечувствительный к регистру выключатель

Java SE 7

сам switch не может быть параметризован как нечувствительный к регистру, но если он абсолютно необходим, он может вести себя нечувствительно к входной строке, используя toLowerCase() или toUpperCase :

switch (myString.toLowerCase()) {
     case "case1" :
        ...            
     break;
     case "case2" :
        ...            
     break;
}

берегись

  • Locale может повлиять на изменение ситуации.
  • Необходимо соблюдать осторожность, чтобы в ярлыках не было символов верхнего регистра - они никогда не будут выполнены!


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow