Java Challengers #2: Сравнение строк
У нас как всегда много опаздывающих к началу курса, так что только вчера провели второе занятие среди нового потока «Разработчик Java». Но это так, мелочи жизни, а пока что мы продолжаем публикацию серии статей Java Challengers, перевод которых подготовили для вас.
В Java класс String инкапсулирует массив char (прим. переводчика — с java 9 это уже массив byte , см. Компактные строки в Java 9). Говоря по простому, String — это массив символов, используемый для составления слов, предложений или других конструкций.
Инкапсуляция — это одна из самых мощных концепций объектно — ориентированного программирования. Благодаря инкапсуляции вам не нужно знать как работает класс String . Вам достаточно знать методы его интерфейса.

Когда вы смотрите на класс String в Java, вы можете увидеть как инкапсулирован массив char :
Чтобы лучше понять инкапсуляцию, представьте физический объект: машину. Нужно ли вам знать, как работает автомобиль под капотом, чтобы управлять им? Конечно, нет, но вы должны знать, что делают интерфейсы автомобиля: педаль газа, тормоза и рулевое колесо. Каждый из этих интерфейсов поддерживает определенные действия: ускорение, торможение, поворот налево, поворот направо. То же самое и в объектно — ориентированном программировании.
Первая статья в серии Java Challengers была про перегрузку методов, которая широко используется в классе String . Перегрузка может сделать ваши классы действительно гибкими:
Вместо того, чтобы пытаться понять, как работает класс String , эта статья поможет вам понять что он делает и как использовать его в вашем коде.
Что такое пул строк (String pool)
Класс String , возможно, наиболее часто используемый класс в Java. Если новый объект создавать в динамической памяти (memory heap) каждый раз, когда мы используем String , то мы потратим впустую много памяти. Пул строк (String pool) решает эту проблему, сохраняя только один объект для каждого значения строки.

Строки в пуле строк
Хотя мы создали несколько переменных String со значениями Duke и Juggy , но в динамической памяти (куче) создаётся и храниться только два объекта. Для доказательства посмотрите следующий пример кода. (Напомним, что в Java оператор » == » используется для сравнения двух объектов и определения того один и тот же это объект или нет.)
Этот код вернет true , потому что две переменные String указывают на один и тот же объект в пуле строк. Их значения одинаковые.
Исключение — оператор new
Теперь посмотрите на этот код — он выглядит похожим на предыдущий пример, но здесь есть отличие.
На основе предыдущего примера можно подумать, что этот код вернёт true , но это не так. Добавление оператора new приводит к созданию нового объекта String в памяти. Таким образом, JVM создаст два разных объекта.
Native-методы в Java — это методы, которые будут компилироваться с использованием языка C, обычно с целью управления памятью и оптимизации производительности.
Пулы строк и метод intern()
Для хранения строк в пуле используется способ, называемый «интернирование строк» (String interning).
Вот, что Javadoc говорит нам о методе intern() :
Метод intern() используется для хранения строк в пуле строк. Во-первых, он проверяет, существует ли уже созданная строка в пуле. Если нет, то создает новую строку в пуле. Логика пула строк основана на паттерне Flyweight.
Теперь, обратите внимание, что происходит, когда мы используем new для создания двух строк:
В отличие от предыдущего примера с ключевым словом new , в данном случае сравнение вернёт true . Это потому, что использование метода intern() гарантирует, что строка будет в пуле.
Метод equals в классе String
Метод equals() используется для того, чтобы проверить одинаковое или нет состояние двух классов. Поскольку equals() находится к классе Object , то каждый Java — класс наследует его. Но метод equals() должен быть переопределен, чтобы он работал правильно. Конечно, String переопределяет equals() .
Как вы видите, значение класса String сравнивается через equals() , а не через ссылку на объект. Не имеет значения, если ссылки на объекты разные; будут сравниваться состояния.
Наиболее распространенные методы String
Есть ещё одна вещь, которую вам нужно знать, прежде чем решить задачку на сравнение строк.
Рассмотрим наиболее распространённые методы класса String :
Решите задачку на сравнение строк
Давайте проверим, что вы узнали о классе String , решив небольшую задачку.
В этой задаче вы сравните несколько строк, используя изученные концепции. Глядя на код ниже, можете ли вы определить значение каждой переменной result ?
Какой будет вывод?
- A: 02468
- B: 12469
- C: 12579
- D: 12568
Правильный ответ приведён в конце статьи.
Что сейчас произошло? Понимание поведения String
В первой строке мы видим:
В этом случае результат false , потому что, когда метод trim() удаляет пробелы он создаёт новый String с помощью оператора new .
Здесь нет никакой тайны, строки одинаковы в пуле строк. Это сравнение возвращает true .
Использование new приводит к созданию двух новых строк и не важно равны их значения или нет. В этом случае сравнение будет false даже если значения одинаковые.
Поскольку мы использовали метод equals() , будет сравниваться значение строки, а не экземпляр объекта.
В этом случае, не имеет значение разные объекты или нет, поскольку сравнивается значение. Результат true .
Окончательно, мы имеем:
Как вы видели ранее, метод intern() помещает строку в пул строк. Обе строки указывают на один и тот же объект, поэтому в этом случае true .
Распространенные ошибки со строками
Бывает трудно определить, указывают ли две строки на один и тот же объект или нет, особенно когда строки содержат одно и то же значение. Полезно помнить, что использование new всегда приводит к созданию нового объекта в памяти, даже если значения строк одинаковые.
Использование методов класса String для сравнения ссылок на объекты также может быть сложным. Особенность в том, что если метод изменяет что-то в строке, то будут разные ссылки на объекты.
Несколько примеров, которые помогут прояснить:
Это сравнение будет истинным, потому что метод trim() не создает новую строку.
В этом случае первый метод trim() генерирует новую строку, так как метод будет выполнять свою работу и поэтому ссылки будут разные.
Наконец, когда trim() выполнит свою работу, он создает новую строку:
Что нужно помнить о строках
Строки не изменяемые, поэтому состояние строки изменить нельзя.
Для экономии памяти JVM хранит строки в пуле строк. При создании новой строки JVM проверяет ее значение и указывает на существующий объект. Если в пуле нет строки с этим значением, то JVM создаёт новую строку.
Оператор » == » сравнивает ссылки на объект. Метод equals() сравнивает значения строк. То же правило будет применяться ко всем объектам.
При использовании оператора new будет создана новая строка в хипе (Прим. переводчика — в оригинале написано, что в пуле, но это не так, спасибо zagayevskiy), даже если есть строка с тем же значением.
Equals в Java и String compare — Сравнение строк

Привет! Сегодня мы поговорим об очень важной и интересной теме, а именно — сравнении объектов между собой equals() в Java. И действительно, в каких случаях в Java Объект А будет равен Объекту Б ? Давай попробуем написать пример: Вывод в консоль: Так, стоп. А почему, собственно, эти две машины не равны? Мы задали им одинаковые свойства, но результат сравнения — false. Ответ прост. Оператор == сравнивает не свойства объектов, а ссылки. Будь у двух объектов даже 500 одинаковых свойств, результатом сравнения все равно будет false. Ведь ссылки car1 и car2 указывают на два разных объекта , на два разных адреса. Представь себе ситуацию со сравнением людей. В мире наверняка есть человек, у которого одинаковые с тобой имя, цвет глаз, возраст, рост, цвет волос и т.д. То есть вы во многом похожи, но все-таки вы не близнецы, и тем более не один и тот же человек. alt=»Equals и сравнение строк — 2″ width=»850″ />Примерно такую логику применяет оператор == , когда с его помощью мы пытаемся сравнить два объекта. Но что, если в твоей программе тебе нужна другая логика? Например, если твоя программа симулирует анализ ДНК. Она должна сравнить код ДНК двух людей, и определить, что это близнецы. Вывод в консоль: Логично, что результат получился тот же самый (ведь мы особо ничего не меняли), но теперь он нас не устраивает! Ведь в реальной жизни анализ ДНК — стопроцентная гарантия того, что перед нами близнецы. Но наша программа и оператор == говорят нам об обратном. Как нам изменить это поведение и сделать так, чтобы в случае совпадения анализов ДНК программа выдавала правильный результат? Для этого в Java был создан специальный метод — equals().
Метод Equals() в Java
String compare в Java — Сравнение строк

Почему мы рассматриваем сравнение строк отдельно от всего остального? Ну, на самом деле, строки в программировании — вообще отдельная песня. Во-первых, если взять все написанные человечеством программы на Java, порядка 25% объектов в них составляют именно они. Поэтому данная тема очень важна. Во-вторых, процесс сравнения строк действительно сильно отличается от остальных объектов. Рассмотрим простой пример: Вывод в консоль: Но почему false? Строки-то ведь абсолютно одинаковые, слово-в-слово :/ Ты можешь предположить: это потому что оператор == сравнивает ссылки! Ведь у s1 и s2 разные адреса в памяти. Если тебя посетила такая мысль, то давай переделаем наш пример: Сейчас у нас тоже две ссылки, но вот результат изменился на противоположный: Вывод в консоль: Окончательно запутался? 🙂 Давай разбираться. Оператор == действительно сравнивает адреса в памяти. Это правило работает всегда и в нем не надо сомневаться. Значит, если s1 == s2 возвращает true, у этих двух строк одинаковый адрес в памяти. И это действительно так! Настало время познакомиться со специальной областью памяти для хранения строк — пулом строк ( String pool ) Пул строк — область для хранения всех строковых значений, которые ты создаешь в своей программе. Для чего он был создан? Как и говорилось раньше, строки занимают огромную часть от всех объектов. В любой большой программе создается очень много строк. С целью экономии памяти и нужен String Pool — туда помещается строка с нужным тебе текстом, и в дальнейшем вновь созданные ссылки ссылаются на одну и ту же область памяти, нет нужды каждый раз выделять дополнительную память. Каждый раз, когда ты пишешь String = “. ” , программа проверяет, есть ли строка с таким текстом в пуле строк. Если есть — новая создана не будет. И новая ссылка будет указывать на тот же адрес в пуле строк, где эта строка хранится. Поэтому когда мы написали в программе ссылка s2 указывает ровно туда же, куда и s1 . Первая команда создала в пуле строк новую строку с нужным нам текстом, а когда дело дошло до второй — она просто сослалась на ту же область памяти, что и s1 . Можно сделать хоть еще 500 строк с таким же текстом, результат не изменится. Стоп. Но почему тогда ранее у нас не сработал этот пример? Думаю, интуитивно ты уже догадываешься в чем причина 🙂 Попробуй предположить, прежде чем читать дальше. Ты видишь, что эти две строки были созданы по-разному. Одна — с помощью оператора new , а вторая без него. Именно в этом кроется причина. Оператор new при создании объекта принудительно выделяет для него новую область в памяти . И строка, созданная с помощью new , не попадает в String Pool : она становится отдельным объектом, даже если ее текст полностью совпадает с такой же строкой из String Pool ’a. То есть если мы напишем такой код: В памяти это будет выглядеть вот так: alt=»Equals и сравнение строк — 4″ width=»850″ />И каждый раз при создании нового объекта через new в памяти будет выделяться новая область, даже если текст внутри новых строк будет одинаковым! С оператором == вроде разобрались, а что с нашим новым знакомым — методом equals()? Вывод в консоль: Интересно. Мы точно знаем, что s1 и s2 указывают на разные области в памяти. Но, тем не менее, метод equals() говорит, что они равны. Почему? Помнишь, выше мы говорили о том, что метод equals() можно переопределить в своем классе, чтобы он сравнивал объекты так, как тебе нужно? С классом String так и поступили. У него есть переопределенный метод equals(). И сравнивает он не ссылки, а именно последовательность символов в строках. И если текст в строках одинаковый, неважно, как они были созданы и где хранятся: в пуле строк, или в отдельной области памяти. Результатом сравнения будет true. Кстати, Java позволяет корректно сравнивать строки без учета регистра. В обычной ситуации, если написать одну из строк, например, капсом, то результатом сравнения будет false: Вывод в консоль: Для этого случая в классе String имеется метод equalsIgnoreCase() . Если в твоем сравнении главным является именно последовательность конкретных символов, а не их регистр, можно применить его. Например, это будет полезно при сравнении двух почтовых адресов: В данном случае очевидно, что речь идет об одном и том же адресе, поэтому использование метода equalsIgnoreCase() будет верным решением.
Class String
Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared. For example:
is equivalent to:
Here are some more examples of how strings can be used:
The class String includes methods for examining individual characters of the sequence, for comparing strings, for searching strings, for extracting substrings, and for creating a copy of a string with all characters translated to uppercase or to lowercase. Case mapping is based on the Unicode Standard version specified by the Character class.
The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings. For additional information on string concatenation and conversion, see The Java Language Specification.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
A String represents a string in the UTF-16 format in which supplementary characters are represented by surrogate pairs (see the section Unicode Character Representations in the Character class for more information). Index values refer to char code units, so a supplementary character uses two positions in a String .
The String class provides methods for dealing with Unicode code points (i.e., characters), in addition to those for dealing with Unicode code units (i.e., char values).
Unless otherwise noted, methods for comparing Strings do not take locale into account. The Collator class provides methods for finer-grain, locale-sensitive String comparison.
Как сравнивать строки в Java?
Следует избегать оператора == ? Когда его можно использовать, а когда нет? В чём разница?
![]()
Оператор == сравнивает ссылки.
Метод equals сравнивает значения.
Следовательно, если вы хотите сравнить строки на равенство, следует использовать equals .
Однако в некоторых случаях строки гарантированно представлены одним и тем же объектом благодаря пулу строк (string interning). Эти случаи явно описаны в спецификации языка Java.
Оператор == используется для проверки, что две строки указывают на один и тот же объект.
Надо отметить, что == заметно быстрее, чем equals (сравнение ссылки вместо вызова метода и посимвольного сравнения, если строки разной длины), поэтому, если вы работаете со строками из пула (или системного, или своего), замена equals на == может привести к заметному ускорению. Но это случается очень редко.
Остерегайтесь вызова equals на null ! Оператор == прекрасно сравнивает строки, если одна или более из них равна null , но вызов метода equals на строке, равной null , приведёт к исключению.
Для сравнения строк, которые могут быть равны null , вы можете воспользоваться следующим методом:
Он присутствует в некоторых сторонних библиотеках, например, в Apache Commons.
Если вы пользуетесь современными средами разработки, то они предупредят, если вы попытаетесь сравнить строки с помощью оператора == . Всегда обращайте внимание на подобные предупреждения.