Java Language
Струны
Поиск…
Вступление
Строки ( 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 до Java 7 пул строк состоялся в особой части кучи, известной как «PermGen». Эта часть собиралась лишь иногда.
В 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 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
.
Более читаемой альтернативой является использование 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, можно использовать следующий алгоритм:
шаги:
- Объявите строку.
- Введите строку.
- Преобразуйте строку в массив символов.
- Введите символ, который нужно искать.
- Поиск символа в массиве символов.
- Если найден, проверьте, имеет ли символ строчный или верхний регистр.
- Если в верхнем регистре добавьте 32 к коду ASCII символа.
- Если в нижнем регистре вычесть 32 из ASCII-кода символа.
- Измените исходный символ из массива символов.
- Преобразуйте массив символов в строку.
Вуаля, Дело о персонаже изменено.
Примером кода для алгоритма является:
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
Чтобы найти точную позицию, в которой строка начинается с другой строки, используйте 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()
возвращает первый индекс 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
Получение длины строки
Чтобы получить длину объекта String
, вызовите на нем метод length()
. Длина равна количеству кодовых единиц UTF-16 (символов) в строке.
String str = "Hello, World!";
System.out.println(str.length()); // Prints out 13
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
. При задании двух параметров первым параметром является начальный символ, а второй параметр - индекс символа сразу после конца (символ в индексе не включен). Легкий способ проверки заключается в том, что вычитание первого параметра из второго должно приводить к ожидаемой длине строки.
В версиях JDK <7u6 метод substring
создает экземпляр String
который имеет один и тот же базовый char[]
в качестве исходной String
и имеет внутренние поля offset
и count
заданные для начала и длины результата. Такое совместное использование может привести к утечкам памяти, что может быть предотвращено вызовом new String(s.substring(...))
для принудительного создания копии, после чего char[]
может быть собран в мусор.
Из 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 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());
вы получите результат, похожий на следующий:
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);
На самом деле 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
Объединение строк с разделителем
Массив строк можно объединить с помощью статического метода 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}"
Реверсивные струны
Есть несколько способов изменить строку, чтобы сделать ее обратно.
StringBuilder / StringBuffer:
String code = "code"; System.out.println(code); StringBuilder sb = new StringBuilder(code); code = sb.reverse().toString(); System.out.println(code);
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
Когда мы используем двойные кавычки для создания String, сначала он ищет String с одинаковым значением в пуле String, если он просто возвращает ссылку else, он создает новую строку в пуле и затем возвращает ссылку.
Однако, используя новый оператор, мы вынуждаем класс String создавать новый объект String в кучном пространстве. Мы можем использовать метод intern (), чтобы поместить его в пул или обратиться к другому объекту String из пула строк, имеющего такое же значение.
Сам пул строк также создается в куче.
До Java 7 String
литералы сохранялись в пуле постоянной среды выполнения в области методов PermGen
, у которых был фиксированный размер.
Пул строк также находился в PermGen
.
В JDK 7 интернированные строки больше не выделяются в постоянном поколении кучи Java, а вместо этого выделяются в основной части кучи Java (так называемые молодые и старые поколения) вместе с другими объектами, созданными приложением , Это изменение приведет к большему количеству данных, находящихся в основной куче Java, и меньше данных в постоянном поколении, и, следовательно, может потребоваться корректировка размеров кучи. Из-за этого большинства приложений будут наблюдаться лишь относительно небольшие различия в использовании кучи, но более крупные приложения, загружающие многие классы или интенсивно использующие метод
String.intern()
будут видеть более значительные различия.
Нечувствительный к регистру выключатель
сам switch
не может быть параметризован как нечувствительный к регистру, но если он абсолютно необходим, он может вести себя нечувствительно к входной строке, используя toLowerCase()
или toUpperCase
:
switch (myString.toLowerCase()) {
case "case1" :
...
break;
case "case2" :
...
break;
}
берегись
-
Locale
может повлиять на изменение ситуации. - Необходимо соблюдать осторожность, чтобы в ярлыках не было символов верхнего регистра - они никогда не будут выполнены!