Skip to content

Latest commit

 

History

History
85 lines (55 loc) · 7.64 KB

File metadata and controls

85 lines (55 loc) · 7.64 KB

Интернирование строк в Java

Содержание:


Что такое интернирование

Интернирование - это техника хранения только одной иммутабельной копии объекта для повторяющихся значений объекта. Каждая такая уникальная копия называется 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), мы можем просто сравнить строки на идентичность за константное время, а не сравнивать символы строк, что заняло бы $O(n)$ даже для идентичных по значению строк.

Однако, не следует сразу бросать equals() и бежать сравнивать идентичность строк по нескольким причинам:

  • Это случай premature optimization
  • Это подвержено ошибкам.

Мы можем заменить вызов equals() на проверку на идентичность (то есть с помощью оператора ==) только в следующих случаях:

  • Мы уверены, что полученные значения отличаются по значению. Например мы берем значения из Set, где по определению лежат только уникальные строки, но нам зачем-то понадобилось сравнить их
  • Мы работаем с Compile-Time константами
  • Мы явно получили intern строки

Метод 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 в целях оптимизации расхода памяти, кроме случаев, когда нам понадобилось явно интернировать строку.