Equals для string java

Java Challengers #2: Сравнение строк

У нас как всегда много опаздывающих к началу курса, так что только вчера провели второе занятие среди нового потока «Разработчик Java». Но это так, мелочи жизни, а пока что мы продолжаем публикацию серии статей Java Challengers, перевод которых подготовили для вас.

В Java класс String инкапсулирует массив char (прим. переводчика — с java 9 это уже массив byte , см. Компактные строки в Java 9). Говоря по простому, String — это массив символов, используемый для составления слов, предложений или других конструкций.

Инкапсуляция — это одна из самых мощных концепций объектно — ориентированного программирования. Благодаря инкапсуляции вам не нужно знать как работает класс String . Вам достаточно знать методы его интерфейса.

Когда вы смотрите на класс String в Java, вы можете увидеть как инкапсулирован массив char :

Чтобы лучше понять инкапсуляцию, представьте физический объект: машину. Нужно ли вам знать, как работает автомобиль под капотом, чтобы управлять им? Конечно, нет, но вы должны знать, что делают интерфейсы автомобиля: педаль газа, тормоза и рулевое колесо. Каждый из этих интерфейсов поддерживает определенные действия: ускорение, торможение, поворот налево, поворот направо. То же самое и в объектно — ориентированном программировании.

Первая статья в серии Java Challengers была про перегрузку методов, которая широко используется в классе String . Перегрузка может сделать ваши классы действительно гибкими:

public String(String original) <> public String(char value[], int offset, int count) <> public String(int[] codePoints, int offset, int count) <> public String(byte bytes[], int offset, int length, String charsetName) <> // И так далее . 

Вместо того, чтобы пытаться понять, как работает класс String , эта статья поможет вам понять что он делает и как использовать его в вашем коде.

Читайте также:  The PURE.CSS Example

Что такое пул строк (String pool)

Класс String , возможно, наиболее часто используемый класс в Java. Если новый объект создавать в динамической памяти (memory heap) каждый раз, когда мы используем String , то мы потратим впустую много памяти. Пул строк (String pool) решает эту проблему, сохраняя только один объект для каждого значения строки.

strings-in-the-string-pool

Хотя мы создали несколько переменных String со значениями Duke и Juggy , но в динамической памяти (куче) создаётся и храниться только два объекта. Для доказательства посмотрите следующий пример кода. (Напомним, что в Java оператор » == » используется для сравнения двух объектов и определения того один и тот же это объект или нет.)

String juggy = "Juggy"; String anotherJuggy = "Juggy"; System.out.println(juggy == anotherJuggy);

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

Исключение — оператор new

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

String duke = new String("duke"); String anotherDuke = new String("duke"); System.out.println(duke == anotherDuke);

На основе предыдущего примера можно подумать, что этот код вернёт true , но это не так. Добавление оператора new приводит к созданию нового объекта String в памяти. Таким образом, JVM создаст два разных объекта.

Native-методы в Java — это методы, которые будут компилироваться с использованием языка C, обычно с целью управления памятью и оптимизации производительности.

Пулы строк и метод intern()

Для хранения строк в пуле используется способ, называемый «интернирование строк» (String interning).

Вот, что Javadoc говорит нам о методе intern() :

 /** * Возвращает каноническое представление для строкового объекта. * * Пул строк (первоначально пустой) управляется классом . * * Когда вызывается метод intern, если пул уже содержит строку, * равную этому объекту , определяемому через * метод , тогда возвращается строка из пула. * Иначе, этот объект добавляется к * пулу и возвращается ссылка на этот объект . * * Из этого следует, что для любых двух строк и , * будет * тогда и только тогда, когда равно . * * Все литеральные строки и строковые константы интернируются. * Строковые литералы определяются в разделе 3.10.5 The Java™ Language Specification. * * @returns строка, которая имеет то же самое содержание как эта строка, * но, гарантируется, что она будет из пула уникальных строк. * * @jls 3.10.5 String Literals */ public native String intern();

Метод intern() используется для хранения строк в пуле строк. Во-первых, он проверяет, существует ли уже созданная строка в пуле. Если нет, то создает новую строку в пуле. Логика пула строк основана на паттерне Flyweight.

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

String duke = new String("duke"); String duke2 = new String("duke"); System.out.println(duke == duke2); // Здесь результат будет false System.out.println(duke.intern() == duke2.intern()); // Здесь результат будет true

В отличие от предыдущего примера с ключевым словом new , в данном случае сравнение вернёт true . Это потому, что использование метода intern() гарантирует, что строка будет в пуле.

Метод equals в классе String

Метод equals() используется для того, чтобы проверить одинаковое или нет состояние двух классов. Поскольку equals() находится к классе Object , то каждый Java — класс наследует его. Но метод equals() должен быть переопределен, чтобы он работал правильно. Конечно, String переопределяет equals() .

public boolean equals(Object anObject) < if (this == anObject) < return true; >if (anObject instanceof String) < String aString = (String)anObject; if (coder() == aString.coder()) < return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); >> return false; >

Как вы видите, значение класса String сравнивается через equals() , а не через ссылку на объект. Не имеет значения, если ссылки на объекты разные; будут сравниваться состояния.

Наиболее распространенные методы String

Есть ещё одна вещь, которую вам нужно знать, прежде чем решить задачку на сравнение строк.

Рассмотрим наиболее распространённые методы класса String :

// Удаляет пробелы в начале и в конце строки trim() // Получает подстроку по индексам substring(int beginIndex, int endIndex) // Возвращает длину строки length() // Заменяет строку, можно использовать регулярное выражение replaceAll(String regex, String replacement) // Проверяет, есть ли указанная последовательность CharSequence в строке contains(CharSequences) 

Решите задачку на сравнение строк

Давайте проверим, что вы узнали о классе String , решив небольшую задачку.

В этой задаче вы сравните несколько строк, используя изученные концепции. Глядя на код ниже, можете ли вы определить значение каждой переменной result ?

public class ComparisonStringChallenge < public static void main(String. doYourBest) < String result = ""; result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; result += "flexibleCode" == "flexibleCode" ? "2" : "3"; result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; System.out.println(result); >>

Правильный ответ приведён в конце статьи.

Что сейчас произошло? Понимание поведения String

result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1";

В этом случае результат false , потому что, когда метод trim() удаляет пробелы он создаёт новый String с помощью оператора new .

result += "flexibleCode" == "flexibleCode" ? "2" : "3";

Здесь нет никакой тайны, строки одинаковы в пуле строк. Это сравнение возвращает true .

result += new String("doYourBest") == new String("doYourBest") ? "4" : "5";

Использование new приводит к созданию двух новых строк и не важно равны их значения или нет. В этом случае сравнение будет false даже если значения одинаковые.

result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7";

Поскольку мы использовали метод equals() , будет сравниваться значение строки, а не экземпляр объекта.

В этом случае, не имеет значение разные объекты или нет, поскольку сравнивается значение. Результат true .

result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9";

Как вы видели ранее, метод intern() помещает строку в пул строк. Обе строки указывают на один и тот же объект, поэтому в этом случае true .

Распространенные ошибки со строками

Бывает трудно определить, указывают ли две строки на один и тот же объект или нет, особенно когда строки содержат одно и то же значение. Полезно помнить, что использование new всегда приводит к созданию нового объекта в памяти, даже если значения строк одинаковые.

Использование методов класса String для сравнения ссылок на объекты также может быть сложным. Особенность в том, что если метод изменяет что-то в строке, то будут разные ссылки на объекты.

Несколько примеров, которые помогут прояснить:

System.out.println("duke".trim() == "duke".trim());

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

System.out.println(" duke".trim() == "duke".trim()); 

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

Наконец, когда trim() выполнит свою работу, он создает новую строку:

// Реализация метода trim в классе String new String(Arrays.copyOfRange(val, index, index + len), LATIN1);

Что нужно помнить о строках

  • Строки не изменяемые, поэтому состояние строки изменить нельзя.
  • Для экономии памяти JVM хранит строки в пуле строк. При создании новой строки JVM проверяет ее значение и указывает на существующий объект. Если в пуле нет строки с этим значением, то JVM создаёт новую строку.
  • Оператор » == » сравнивает ссылки на объект. Метод equals() сравнивает значения строк. То же правило будет применяться ко всем объектам.
  • При использовании оператора new будет создана новая строка в хипе (Прим. переводчика — в оригинале написано, что в пуле, но это не так, спасибоzagayevskiy), даже если есть строка с тем же значением.

Ответ

Ответ на эту задачу — D. Вывод будет 12568.

Источник

Оцените статью