Originally Posted on https://chetangupta.net/strings/
В любом языке программирования для выполнения любого вида вычислений вы всегда работаете с числовыми (Int, float, double и т.д.), булевыми и строковыми типами данных. Они считаются основными или примитивными типами данных, которые необходимы даже для создания сложных структур/классов, и их детальное знание всегда поможет вам разработать лучший код и выбрать правильную комбинацию данных для получения точных результатов от ваших функций/программ.
В этой статье мы погрузимся в концепцию String. Поймем, как они работают в Java, и какие операции над ними можно выполнять, так что без лишних слов приступим!
- Что такое строка в java?
- Как создать строковый объект?
- Операции и методы строк:
- Длина строки:
- Конкатенация/добавление строк:
- SubString
- Содержит
- Объединение строк :
- Сравнение со строками:
- Корпус в строке :
- Удаление белых пробелов :
- Заменить символ в строке:
- Конкатенация строк с помощью оператора «+»
- Какой оператор лучше использовать?
- Создание форматных строк
- Символ Escape в строках
- Строки Java неизменяемы
- Бонус: решение загадки
- Заключение
Что такое строка в java?
Первый вопрос, который должен прийти вам в голову — что это такое? и как они выглядят? И ответ на него очень прост: как и в английской грамматике, строка — это не что иное, как коллекция символов (алфавитов и символов), собранных вместе. Это не строка, которая имеет смысл или нет, до тех пор, пока группа символов не станет строкой. Например, «Это образец строки», или даже «zxczxvc adsad qwe» — это тоже строка.
Разные языки программирования по-разному представляют строку, например, некоторые представляют ее как «hello world» или «привет мир». Следует отметить, что строка может начинаться с одинарных кавычек — ‘ или двойных кавычек — «». Кроме того, в силу особенностей языка, существует возможность того, что язык не поддерживает типы строк напрямую, он может поддерживать символы как примитивный тип, и строки могут быть сформированы комбинацией этих символов. Если язык поддерживает символьный тип, то символ всегда начинается с одинарных кавычек, а строка будет начинаться с двойных кавычек.
В любом случае, хватит о предыстории, вернемся к языку, который мы здесь изучаем, а это TADA… Java, String здесь не является примитивным типом данных, даже если он поставляется в комплекте с языком. Java поддерживает символьный тип, то есть тип данных char
. Давайте рассмотрим его на примере:
Пример символьного типа:
char thisIsa = 'a';
char thisIsA = 'A';
К вашему сведению thisIsa
не равно thisIsA
. О символах можно многое узнать, но мы не будем углубляться в это (вы можете узнать больше о них здесь → https://chetangupta.net).
Пример типа String :
String thisIsA = "A";
String thisIsa = "a";
То же самое и здесь thisIsa
не равно thisIsA
, так как строки чувствительны к регистру. Надеюсь, теперь вы поняли суть и некоторые сведения о строках…
Если да, то загадайте мне загадку?
char thisIsA = 'A';
String thisIsAlsoA = "A";
Поскольку мы сказали, что строка — это набор последовательности символов, будет ли переменная String thisIsAlsoA
равна thisIsA
? Если вы достаточно любопытны, подумайте, если не смогли найти ответ до конца статьи…
Как создать строковый объект?
Давайте углубимся в процесс создания String в Java. То, как мы создали строку с помощью двойных кавычек, называется созданием String Literal
.
Пример
String a = "A";
String greet = "Hello World!";
String pokemon = "Pikachu";
Другим способом создания является использование String Object
, поскольку мы знаем, что в Java создание объекта происходит с помощью оператора new
, таким образом, мы создадим строку типа String apple = new String(
"
apple
"
);
.
Еще один пример:
String a = new String("A");
String greet = new String("Hello World!");
String pokemon = new String("Pikachu");
Вы можете подумать, в чем разница, а также почему я должен создавать его с помощью метода Object, который выглядит более длинным? Ну, для этого нужно понять, что на самом деле происходит под капотом в модели памяти Java.
Когда мы создаем String Literal, JVM ищет в String Pool, чтобы проверить, существует ли уже такое значение или нет. Если оно найдено, то возвращается ссылка на объект, если нет, то создается новый строковый объект с заданным значением и сохраняется в пуле строк.
P.S : Чтобы узнать больше о пуле строк, посетите сайт https://chetangupta.net.
Когда мы создаем объект с помощью оператора new
, то происходит две вещи: значение строки, которое мы передали, проверяется и помещается в String pool, но объект String создается в куче памяти. Таким образом, этот метод займет больше времени на выполнение, а также следует помнить о том, что каждый раз будет создаваться новый объект.
Хорошо, знать эту историю хорошо, но как она объясняет пример использования оператора new
?
Давайте разберемся:
Строковый литерал — это строковый объект, но строковый объект не всегда является строковым литералом. Литерал также представляет фиксированные значения, т.е. константы, это означает, что созданный однажды строковый литерал не может быть изменен, поэтому часто говорят, что строки Java неизменяемы. мы поговорим о них позже. Но String Object отличается тем, что его можно изменять.
Поэтому всякий раз, когда мы имеем дело со строкой, требующей большого количества модификаций, мы будем предпочитать строковые объекты строковым литералам.
Всегда предпочитайте использовать StringBuffers
или StringBuilders
для создания строковых объектов, которые требуют много модификаций. Выбирайте StringBuilders
, потому что они современнее StringBuffers и обладают большей функциональностью.
Пример
StringBuilder greetBuilder = new StringBuilder("");
greetBuilder.append("hello");
greetBuilder.append(" ");
greetBuilder.append("World");
greetBuilder.append("!");
String greet = greetBuilder.toString();
System.out.println(greet) // output : hello World!
Надеюсь, теперь вы понимаете роль строковых объектов в сравнении со строковыми литералами.
Операции и методы строк:
Теперь наступает интересная часть, давайте изучим, какие возможные операции мы можем выполнить над типом данных String:
Длина строки:
Если вы хотите узнать длину строки, то есть общее количество символов в строке, то используйте функцию length()
над примером строки :
String apple = "Apple";
println(apple.length()); // output --> 5
Character at Index :
Поскольку строка представляет собой последовательность символов, мы можем получить символ по заданному индексу. Поэтому мы можем получить символ по индексу, используя функцию charAt(index)
.
Пример
// if we want to get `l` from string apple then
String apple = "Apple";
println(apple.charAt(3)); // output --> `l`
Примечание: Следует понимать, что строки неизменяемы, т.е. вы можете прочитать значение по индексу, но не можете обновить значение по данному индексу, т.е. для строки не существует функции setCharAt(index)
.
Конкатенация/добавление строк:
Мы можем сложить две строки, т.е. также называемые конкатенацией, в одну строку с помощью функции concat()
.
Примечание : новая конкатенированная строка, которую мы получим после операции, будет новой строкой, потому что строки неизменяемы.
Пример :
String apple = "Apple";
String orange = "Orange";
println(apple.concat(orange)); // output --> AppleOrange
println(orange.concat(apple)); // output --> OrangeApple
SubString
Подстрока означает выделение части строки по заданным индексам. Для этого существует два перегруженных метода функций:
Пример :
String orange = "Orange";
println(orange.substring(/*StartIndex*/4)); // output --> ge
println(orange.substring(/*StartIndex*/1,/*EndIndex*/5)); // output --> rang
Содержит
Мы используем Contains, чтобы проверить, содержит ли строка другую строку или нет. Примечание: в фоновом режиме он проверяет CharSequence для более быстрой оценки, чем проверка каждого слова. (подробнее об этом можно узнать здесь: https://chetangupta.net.
Пример :
String orange = "Orange";
println(orange.contains("range")); // output --> true
println(orange.contains("apple")); // output --> false
Объединение строк :
Join, как следует из названия, соединяет две строки, но вы можете задаться вопросом, в чем разница между join и concat?
- Просто посмотрите на параметры, которые они принимают, для Join вы можете передать ‘n’ количество строк, а для concat это будет работать только с двумя строками,
- Join также может предоставить разделитель/сепаратор, т.е. символ, который будет использоваться при объединении строк.
- В отличие от concat, Join является статической функцией над классом String.
Пример:
String apple = "Apple";
String orange = "Orange";
String berry = "StrawBerries";
String seperator = "|";
println(String.join(seperator,apple,orange,berry));
// output --> Apple|Orange|StrawBerries
Сравнение со строками:
Чтобы вы знали, когда мы выполняем операцию сравнения, мы цензурируем, равны ли два элемента, первый элемент больше(>) второго элемента или первый элемент меньше(<) второго элемента.
В программировании функция сравнения возвращает целочисленное значение для представления одинаковых сценариев.
if item1 == item2 then returns 0,
if item1 > item2 then returns 1,
if item1 < item2 then returns -1,
В строке эти сравнения основаны на лексикографическом (словарном) порядке, т.е.
if string1 == string2 then returns 0
if string1 > string2 then returns 1, (string 1 comes ahead in dictinoary order)
if string1 < string2 then returns -1, (string 2 comes ahead in dictinoary order)
В Java у нас есть две функции для сравнения строк в Java.
Рассмотрим пример:
// case sensitive comparison
String apple = "Apple";
println(apple.compareTo("Apple")); // output --> 0
println(apple.compareTo("apple")); // output --> -32
// case in-sensitive comparison
String apple = "Apple";
println(apple.compareToIgnoreCase("Apple")); // output --> 0
println(apple.compareToIgnoreCase("apple")); // output --> 0
Корпус в строке :
Мы можем контролировать регистр символов нашей строки с помощью двух функций:
String apple = "ApPlE";
println(apple); // output --> ApPlE
println(apple.toUpperCase()); // output --> APPLE
println(apple.toLowerCase()); // output --> apple
Удаление белых пробелов :
Строковые значения часто могут содержать пробелы в начале или в конце. Примечание: Пробелы между символами не являются белым пространством. Мы можем использовать функцию trim()
для их удаления.
String appleTrimmed = " apple ".trim();
println(appleTrimmed) // output --> apple
println(appleTrimmed.length()) // output --> 5
Заменить символ в строке:
Если вы хотите заменить символ со старого символа на новый.
Примечание: Помните, что замена символа в строке — это не тот же процесс, что и установка символа по некоторому индексу в строке. Правило о том, что строки неизменяемы, будет действовать и здесь, поэтому при попытке заменить символ вы получите новую строку с замененным символом.
Пример :
String apple = "apple";
println(apple.replace("a","pinea")); // output --> pineapple
Конкатенация строк с помощью оператора «+»
Мы уже рассмотрели два способа объединения двух строк, один из которых — concat, а другой — join, но очень распространенным шаблоном, который вы можете встретить для объединения двух строк, является оператор «+».
Использование оператора «+» работает точно так же, как и функция concat. Пример
String apple = "apple";
String orange = "Orange";
String berry = "StrawBerries";
println(apple.concat(orange).concat(berry)); // output --> appleOrangeStrawBerries
println(apple + orange + berry); // output --> appleOrangeStrawBerries
Какой оператор лучше использовать?
На наш взгляд, если сравнивать «concat()» и «+», то оператор «+» кажется более читаемым, но будьте осторожны, используя его с данными числового типа.
Пример
int orangeCount = 5;
int berryCount = 5;
println("fruits count:" + orangeCount + berryCount);
// wrong output --> fruits count:55
println("fruits count:" + (orangeCount + berryCount));
// right output --> fruits count:10
Но, как мы предположили, это та же проблема, что и с concat, поскольку строки неизменяемы, мы фактически создаем новую строку, лучшим и более быстрым способом будет использование StringBuilder.
Создание форматных строк
Мы рассмотрели многие основные функциональные возможности строки, и мы можем ясно видеть сценарий использования, в котором мы добавляем строки со строками, float, числами и т.д… что если мы можем иметь значения-заполнители в строке, а во время выполнения эти значения-заполнители могут быть заменены значениями, которые мы хотим поместить?
Именно это и делает функция String.format()
. Мы создаем строку с заполнителями/спецификаторами и заполняем их значением, мы также называем такой тип строк шаблонными строками.
Мы используем различные спецификаторы для разных типов данных, чаще всего используются следующие:
- %b для boolean,
- %s для строк,
- %d для целых чисел
- %f для чисел с плавающей запятой
и другие, о которых вы можете прочитать здесь: https://chetangupta.net/.
Пример :
println(String.format("%s has %d apple, and %d oranges", "John Doe", 3, 2));
// output --> John Doe has 3 apple, and 2 oranges
Примечание : значения помещаются в позицию шаблона в соответствии с их вхождением в параметр функции,
По сравнению с оператором concat или plus этот метод полезен, когда вы работаете с очень длинными строками. По производительности он не так хорош, как StringBuilder, поскольку ему необходимо разобрать всю строку и параметры перед генерацией строки.
Символ Escape в строках
Когда мы работаем с длинной строкой, если мы попытаемся разбить ее на две части, самое очевидное, что придет вам в голову, это нажать клавишу Enter и продолжить строку в следующей строке, но если вы попытаетесь сделать это, компилятор выдаст вам синтаксическую ошибку, например :
String message = "
I want to make
very lengthy String!
";
Чтобы сделать такую функциональность, необходимо использовать специальные символы n
в строке для добавления новой строки в строку.
String message = "nI want to makenvery lengthy String!";
/*
* Output :
* I want to make
* very lengthy String!
*/
Escaped Character :
t → add horizontal tab
n → add new line
’ → add single quotes in String
” → add double quotes in String
\ → add backslash in the String
Примеры
String message = "HellotWorld!";
println(message); // output --> Hello World!
message = "HellonWorld!";
println(message);
/*
* Output -->
* Hello
* World!
*/
message = "Hello'World'!";
println(message); // output --> Hello'World'!
message = "Hello"World!"";
println(message); // output --> Hello"World!"
message = "Hello\World!\";
println(message); // output --> HelloWorld!
Строки Java неизменяемы
Если вы внимательно прочитали всю статью, то вы заметили, что мы повторяем это утверждение снова и снова. Это потому, что это основной механизм, с помощью которого функционируют строки Java.
Ключевой причиной того, что строки являются неизменяемыми, являются преимущества кэширования, производительности и безопасности.
Строка является наиболее широко используемой структурой данных в любой программе, и JVM оптимизирует объем памяти, выделяемой для них, храня только одну копию каждой строки в пуле строк.
Таким образом, кэширование литералов String и их повторное использование экономит много места в куче, поскольку разные переменные String ссылаются на один и тот же объект в пуле String, экономя важнейшие ресурсы памяти и повышая производительность приложения в целом.
Дополнительным преимуществом неизменяемости строк является их потокобезопасность, поэтому строки в параллельной среде безопасны для чтения и обновления.
Бонус: решение загадки
char thisIsA = 'A';
String thisIsAlsoA = "A";
равен ли этотIsAlsoA
этомуIsA
? такова была наша загадка.
Если вы думаете, что да, то, извините, вы ошибаетесь. Концептуально вы правы, это должно быть правильно, но проблема в том, что Java не является динамически типизированным языком, он статически типизированный. Таким образом, типы играют здесь главную роль. Поскольку типы String и char не совпадают, произошла ошибка. Это не произошло бы в таком языке, как JavaScript.
Еще одна вещь… Строка — это коллекция символов, правильно? Таким образом, мы можем преобразовать нашу строку в массив символов с помощью toCharArray()
, поскольку массив — это коллекция, что программно оправдывает наше утверждение, и если мы сравним первую позицию индекса массива с символом, то они будут равны. Отлично!
Заключение
Давайте подведем небольшой итог:
- Мы узнали, что строка — это коллекция последовательности символов.
- В Java мы можем создавать строки двумя способами — строковые литералы и строковые объекты.
- Существуют различные операции, которые мы можем выполнять над строками — объединение, длина, сравнение и многие другие.
- Что такое специальные управляющие символы в строках
- И самое главное — почему строки неизменяемы!
Мы надеемся, что вы узнали много нового и раскрыли все тайны и сомнения вокруг Java String. До следующего раза, счастливого взлома!