Сравнение строк: == vs equals()
Условие
Предскажите результат выполнения следующего кода:
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s1 == s2); // ?
System.out.println(s1 == s3); // ?
System.out.println(s1.equals(s3)); // ?
System.out.println(s3 == s4); // ?
System.out.println(s3.equals(s4)); // ?
Вопросы
- Каков результат каждого сравнения?
- Что такое String Pool?
- В чём разница между == и equals()?
- Когда использовать == и когда equals()?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Сравнение строк: == vs equals()
Это один из самых частых вопросов на собеседованиях Java-разработчиков, и ответ требует глубокого понимания того, как Java работает со строками. Давайте разберёмся с примерами.
Результаты выполнения кода
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s1 == s2); // true ✅
System.out.println(s1 == s3); // false ❌
System.out.println(s1.equals(s3)); // true ✅
System.out.println(s3 == s4); // false ❌
System.out.println(s3.equals(s4)); // true ✅
Почему такие результаты?
Сравнение s1 == s2 → true
Обе переменные указывают на один и тот же объект в String Pool:
┌─────────────────────────┐
│ String Pool (Heap) │
├─────────────────────────┤
│ "Hello" (объект) │◄─── s1
└─────────────────────────┘ s2
Когда вы создаёте строку с использованием литерала (например, "Hello"), Java проверяет, существует ли такая строка в String Pool. Если существует — переиспользует существующий объект.
Сравнение s1 == s3 → false
s3 создаётся с помощью new String(), что создаёт новый объект в Heap:
┌─────────────────────────┐
│ String Pool (Heap) │
├─────────────────────────┤
│ "Hello" (объект 1) │◄─── s1
└─────────────────────────┘
┌─────────────────────────┐
│ Heap (память) │
├─────────────────────────┤
│ "Hello" (объект 2) │◄─── s3
└─────────────────────────┘
Оператор == сравнивает ссылки (адреса в памяти), а не содержимое. Разные объекты = разные ссылки = false.
Сравнение s1.equals(s3) → true
Метод equals() сравнивает содержимое строк, а не их ссылки:
public boolean equals(Object anObject) {
if (this == anObject) {
return true; // Если ссылки одинаковые
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = this.value.length;
if (n == anotherString.value.length) {
// Сравниваем каждый символ
char v1[] = this.value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Да, содержимое одинаковое → true.
Сравнение s3 == s4 → false
Оба объекта созданы с new, каждый раз создаётся новый объект в Heap:
┌─────────────────────────┐
│ Heap (память) │
├─────────────────────────┤
│ "Hello" (объект 2) │◄─── s3
│ "Hello" (объект 3) │◄─── s4
└─────────────────────────┘
Разные объекты = разные ссылки → false.
Сравнение s3.equals(s4) → true
Оба объекта содержат одинаковый текст → true.
Что такое String Pool?
String Pool (также называемый String Intern Pool) — это специальная область памяти (часть Heap), где Java хранит уникальные строковые литералы.
String a = "Java"; // "Java" добавляется в pool
String b = "Java"; // Переиспользуется из pool
String c = new String("Java"); // Новый объект вне pool
System.out.println(a == b); // true (одна ссылка из pool)
System.out.println(a == c); // false (разные объекты)
System.out.println(a.equals(c)); // true (одинаковое содержимое)
Зачем нужен String Pool?
- Оптимизация памяти — строки часто повторяются
- Производительность — переиспользование экономит память
- Сравнение —
==быстрее работает со ссылками
Различие между == и equals()
| Оператор | Что сравнивает | Скорость | Использование |
|---|---|---|---|
| == | Ссылки (адреса) | Быстро O(1) | Null-проверки, идентичность |
| equals() | Содержимое | Медленнее O(n) | Сравнение значений |
Правильное сравнение строк
// ❌ НЕПРАВИЛЬНО — может вернуть false
if (userInput == "admin") {
// Не будет работать!
}
// ✅ ПРАВИЛЬНО — сравнивает содержимое
if (userInput.equals("admin")) {
// Правильно!
}
// ✅ ПРАВИЛЬНО — игнорирует регистр
if (userInput.equalsIgnoreCase("ADMIN")) {
// Также правильно!
}
// ✅ ПРАВИЛЬНО — null-safe сравнение (Java 11+)
if (Objects.equals(userInput, "admin")) {
// Не выбросит NullPointerException
}
Когда использовать что?
Используйте ==:
String s = null;
if (s == null) { // ✅ Правильно
System.out.println("Null!");
}
if (s.equals(null)) { // ❌ NullPointerException!
System.out.println("Null!");
}
Используйте equals():
String username = "john_doe";
if (username.equals("john_doe")) { // ✅ Правильно
System.out.println("Welcome!");
}
if (username == "john_doe") { // ❌ Может быть false
System.out.println("Welcome!");
}
Производительность
String s1 = "Hello World".intern(); // Добавить в pool
String s2 = new String("Hello World").intern(); // Получить из pool
long start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
boolean result = s1 == s2; // Очень быстро
}
long time1 = System.nanoTime() - start;
start = System.nanoTime();
for (int i = 0; i < 1_000_000; i++) {
boolean result = s1.equals(s2); // Медленнее
}
long time2 = System.nanoTime() - start;
System.out.println("== время: " + time1);
System.out.println("equals() время: " + time2);
// Обычно == примерно в 10 раз быстрее!
Метод intern()
String s1 = new String("Hello");
String s2 = "Hello";
System.out.println(s1 == s2); // false
String s3 = s1.intern(); // Добавляем в String Pool
System.out.println(s3 == s2); // true
Метод intern() добавляет строку в String Pool и возвращает ссылку на объект в pool.
Итоговые правила
- Для сравнения содержимого строк: используйте
equals()илиequalsIgnoreCase() - Для проверки на null: используйте
== - Для идентичности объектов: используйте
== - Никогда не полагайтесь на
==для сравнения строк (кроме null-проверок) - Если производительность критична → используйте
intern(), но с осторожностью