Содержание:
- Что такое интернирование
- Зачем это нужно
- Снижение расходов памяти
- Сравнение строк
- Метод intern()
- Проверим на практике
- Заключение
Интернирование - это техника хранения только одной иммутабельной копии объекта для повторяющихся значений объекта. Каждая такая уникальная копия называется intern-ом.
Интернирование строк же означает, что всегда будет создаваться и храниться только одна уникальная копия строки для строк с одинаковым содержимым.
Интернирование строк позволяет оптимизировать работу с строками как в аспекте времени, так и памяти.
Если в коде объявлено две одинаковые по значению literal строки, то на самом деле ссылки будут указывать на один и тот же объект, так что оператор == вернет true при их сравнении.
JVM использует так называемый Java String Pool - специальный регион в куче, где хранятся все уникальные копии строк. JVM интернирует по-умолчанию все строки объявленные как literal (Compile-Time Constants). Например "Hello, World!"" будет интернирован, но не new String("Hello, World!"), потому что вызов new всегда возвращает новый объект.
Garbage Collector также чистит и String Pool, поэтому те строки, которые больше не используются в приложении, будут очищены из памяти.
Когда мы уверены, что ссылки ведут на объект в String Pool (на intern), мы можем просто сравнить строки на идентичность за константное время, а не сравнивать символы строк, что заняло бы
Однако, не следует сразу бросать equals() и бежать сравнивать идентичность строк по нескольким причинам:
- Это случай premature optimization
- Это подвержено ошибкам.
Мы можем заменить вызов equals() на проверку на идентичность (то есть с помощью оператора ==) только в следующих случаях:
- Мы уверены, что полученные значения отличаются по значению. Например мы берем значения из
Set, где по определению лежат только уникальные строки, но нам зачем-то понадобилось сравнить их - Мы работаем с Compile-Time константами
- Мы явно получили intern строки
Получить intern строки, а также интернировать строку мы можем с помощью вызова метода intern(). Данный метод проверяет, не содержится ли в String Pool уже такая строка, и возвращает её, а если не содержится, то создает её и кладет в String Pool и только затем возвращает.
Вызов intern() для literal строки не будет иметь эффекта и вернет тот же самый объект, однако с помощью этого метода мы можем вручную интернировать строки, созданные с помощью вызова new.
Проверим же высказанные выше утверждения на практике.
Сравним строки:
String s1 = Hello;
String s2 = Hello;
String s3 = new String(Hello);
String s4 = s3.intern();
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1 == s4); // trueМы видим, что:
- Сравнение строковых констант вернуло true, как и ожидалось, так как ссылки s1 и s2 указывают на один и тот же объект строки в String Pool
- Сравнение строковой константы и строки созданной с помощью
newвернуло false, так как ссылки s1 и s3 указывают на разные объекты - Сравнение строковой константы и intern() строки созданной с помощью
newвернуло true, так как они являются одним и тем же объектом в String Pool
Итак, мы изучили, что такое интернирование строк в Java. Следует заметить, что в Java интернируются не только строки, но и Boxed типы для следующих примитивов:
- boolean: смотрите константы
Boolean.TRUEиBoolean.FALSE- Boolean - byte: cм. класс
ByteCacheв классе Byte - char: гарантированно стандартом для значений от 0 до 127 (см. класс
CharacterCacheв классе Character) - short и int: гарантированно стандартом для значений от -128 до 127 (см. классы
ShortCacheв классе Short иIntegerCacheв Integer)
Так, например, выражение Integer.valueOf(1) == Integer.valueOf(1) возвращает true.
Использовать intern-ы строк напрямую (полученные с помощью вызова метода intern()) вам скорее всего не придется, так как, опять же, сравнение строк на идентичность - это плохо и лучше всегда использовать equals(), а другого случая использования интернов напрямую нет. Само же интернирование нужно в основном только JVM в целях оптимизации расхода памяти, кроме случаев, когда нам понадобилось явно интернировать строку.