Что такое пул строк в Java?
Я запутался в StringPool в Java. Я сталкивался с этим, читая главу о строках в Java. Пожалуйста, помогите мне понять, с точки зрения непрофессионала, что на самом деле делает StringPool.
4 ответа
Это печатает true
(хотя мы не используем equals
Метод: правильный способ сравнения строк)
String s = "a" + "bc";
String t = "ab" + "c";
System.out.println(s == t);
Когда компилятор оптимизирует ваши строковые литералы, он видит, что оба s
а также t
имеют одинаковое значение и, следовательно, вам нужен только один строковый объект. Это безопасно, потому что String
неизменен в Java.
В результате оба s
а также t
указать на тот же объект и немного сэкономленной памяти.
Название "строковый пул" исходит из того, что все уже определенные строки хранятся в некотором "пуле" и перед созданием новой String
Компилятор объектов проверяет, была ли такая строка уже определена.
Я не думаю, что это на самом деле много, похоже, это просто кеш для строковых литералов. Если у вас есть несколько строк, значения которых совпадают, все они будут указывать на один и тот же строковый литерал в пуле строк.
String s1 = "Arul"; //case 1
String s2 = "Arul"; //case 2
В случае 1 литерал s1 создается заново и хранится в пуле. Но в случае 2, литерал s2 ссылается на s1, он не будет создавать новый.
if(s1 == s2) System.out.println("equal"); //Prints equal.
String n1 = new String("Arul");
String n2 = new String("Arul");
if(n1 == n2) System.out.println("equal"); //No output.
Давайте начнем с цитаты из спецификации виртуальной машины:
Загрузка класса или интерфейса, который содержит литерал String, может создать новый объект String (§2.4.8) для представления этого литерала. Это может не произойти, если объект String уже создан для представления предыдущего вхождения этого литерала или если метод String.intern был вызван для объекта String, представляющего ту же строку, что и литерал.
Это может не произойти - это намек, что есть что-то особенное в String
объекты. Обычно вызов конструктора всегда создает новый экземпляр класса. Это не относится к Strings, особенно когда объекты String "создаются" с литералами. Эти строки хранятся в глобальном хранилище (пуле) - или, по крайней мере, ссылки хранятся в пуле, и всякий раз, когда требуется новый экземпляр уже известных строк, vm возвращает ссылку на объект из пула. В псевдокоде это может выглядеть так:
1: a := "one"
--> if(pool[hash("one")] == null) // true
pool[hash("one") --> "one"]
return pool[hash("one")]
2: b := "one"
--> if(pool[hash("one")] == null) // false, "one" already in pool
pool[hash("one") --> "one"]
return pool[hash("one")]
Так что в этом случае переменные a
а также b
хранить ссылки на один и тот же объект. В этом случае мы имеем (a == b) && (a.equals(b)) == true
,
Это не тот случай, если мы используем конструктор:
1: a := "one"
2: b := new String("one")
Снова, "one"
создается в пуле, но затем мы создаем новый экземпляр из того же литерала, и в этом случае это приводит к (a == b) && (a.equals(b)) == false
Так почему у нас есть пул строк? Строки и особенно строковые литералы широко используются в типичном коде Java. И они неизменны. И неизменность позволила кэшировать String для экономии памяти и увеличения производительности (меньше усилий для создания, меньше мусора для сбора).
Как программисты, нам не нужно заботиться о пуле String, если мы помним:
(a == b) && (a.equals(b))
может бытьtrue
или жеfalse
(всегда используйтеequals
сравнить строки)- Не используйте отражение, чтобы изменить поддержку
char[]
строки (поскольку вы не знаете, кто задействует эту строку)
Когда JVM загружает классы или иным образом видит литеральную строку или какой-то код intern
Строка, она добавляет строку в скрытую в основном справочную таблицу, в которой есть одна копия каждой такой строки. Если добавляется другая копия, среда выполнения упорядочивает ее так, чтобы все литералы ссылались на один и тот же строковый объект. Это называется "интернирование". Если вы говорите что-то вроде
String s = "test";
return (s == "test");
это вернется true
потому что первый и второй "тест" на самом деле один и тот же объект. Сравнение интернированных строк таким способом может быть намного, намного быстрее, чем String.equals
, так как есть одно сравнение, а не куча char
сравнения.
Вы можете добавить строку в пул, вызвав String.intern()
, который вернет вам объединенную версию строки (которая может быть той же самой строкой, которую вы интернируете, но вы с ума сошли бы, полагаясь на это - вы часто не можете точно знать, какой код был загружен и бегите до сих пор и интернируйте ту же строку). Объединенная версия (строка, возвращенная из intern
) будет равен любому одинаковому литералу. Например:
String s1 = "test";
String s2 = new String("test"); // "new String" guarantees a different object
System.out.println(s1 == s2); // should print "false"
s2 = s2.intern();
System.out.println(s1 == s2); // should print "true"