Когда нам следует использовать метод intern строки String для литералов String
Согласно String # intern (), intern
Предполагается, что метод возвращает строку из пула строк, если строка найдена в пуле строк, в противном случае новый объект строки будет добавлен в пул строк и будет возвращена ссылка на эту строку.
Итак, я попробовал это:
String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
if ( s1 == s2 ){
System.out.println("s1 and s2 are same"); // 1.
}
if ( s1 == s3 ){
System.out.println("s1 and s3 are same" ); // 2.
}
Я ожидал, что s1 and s3 are same
будет напечатан как s3 интернирован, и s1 and s2 are same
не будет напечатан. Но результат: обе строки напечатаны. Это значит, что по умолчанию строковые константы интернированы. Но если это так, то зачем нам intern
метод? Другими словами, когда мы должны использовать этот метод?
13 ответов
Java автоматически интернирует строковые литералы. Это означает, что во многих случаях оператор == работает для строк так же, как и для целочисленных значений или других примитивных значений.
Поскольку интернирование происходит автоматически для строковых литералов, intern()
метод должен использоваться на строках, построенных с new String()
Используя ваш пример:
String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
String s4 = new String("Rakesh");
String s5 = new String("Rakesh").intern();
if ( s1 == s2 ){
System.out.println("s1 and s2 are same"); // 1.
}
if ( s1 == s3 ){
System.out.println("s1 and s3 are same" ); // 2.
}
if ( s1 == s4 ){
System.out.println("s1 and s4 are same" ); // 3.
}
if ( s1 == s5 ){
System.out.println("s1 and s5 are same" ); // 4.
}
вернусь:
s1 and s2 are same
s1 and s3 are same
s1 and s5 are same
Обратитесь к JavaTechniques "Строковое Равенство и Стажировка" для получения дополнительной информации.
В недавнем проекте, некоторые огромные структуры данных были созданы с данными, которые считывались из базы данных (и, следовательно, не константами / литералами String), но с огромным количеством дублирования. Это было банковское приложение, и повсюду появились такие вещи, как названия скромных корпораций (возможно, 100 или 200). Структуры данных уже были большими, и если бы все эти имена корпусов были уникальными объектами, они бы переполнили память. Вместо этого все структуры данных имели ссылки на одни и те же 100 или 200 объектов String, что позволило сэкономить много места.
Еще одно небольшое преимущество интернированных строк в том, что ==
может использоваться (успешно!) для сравнения строк, если все участвующие строки гарантированно интернированы. Помимо упрощенного синтаксиса, это также повышение производительности. Но, как отмечали другие, выполнение этого таит в себе большой риск появления ошибок программирования, поэтому это следует делать только как отдельную меру крайней меры.
Недостатком является то, что интернирование String занимает больше времени, чем простое его выбрасывание в кучу, и что пространство для интернированных строк может быть ограничено в зависимости от реализации Java. Лучше всего это делать, когда вы имеете дело с известным разумным количеством строк со множеством дубликатов.
Я хочу добавить свои 2 цента при использовании ==
с интернированными строками.
Во-первых String.equals
делает это this==object
,
Таким образом, хотя есть небольшой прирост производительности (вы не вызываете метод), с точки зрения сопровождающего, используя ==
это кошмар, потому что некоторые интернированные строки имеют тенденцию становиться не интернированными.
Поэтому я предлагаю не полагаться на особый случай ==
для интернированных строк, но всегда используйте equals
как и задумал Гослинг.
РЕДАКТИРОВАТЬ: интернированный становится не интернированным:
V1.0
public class MyClass
{
private String reference_val;
...
private boolean hasReferenceVal ( final String[] strings )
{
for ( String s : strings )
{
if ( s == reference_val )
{
return true;
}
}
return false;
}
private void makeCall ( )
{
final String[] interned_strings = { ... init with interned values ... };
if ( hasReference( interned_strings ) )
{
...
}
}
}
В версии 2.0 сопровождающий решил сделать hasReferenceVal
public, не вдаваясь в подробности, что он ожидает массив интернированных строк.
V2.0
public class MyClass
{
private String reference_val;
...
public boolean hasReferenceVal ( final String[] strings )
{
for ( String s : strings )
{
if ( s == reference_val )
{
return true;
}
}
return false;
}
private void makeCall ( )
{
final String[] interned_strings = { ... init with interned values ... };
if ( hasReference( interned_strings ) )
{
...
}
}
}
Теперь у вас есть ошибка, которую может быть очень трудно найти, потому что в большинстве случаев массив содержит литеральные значения, а иногда используется не-литеральная строка. Если equals
были использованы вместо ==
затем hasReferenceVal
все равно продолжал бы работать. Еще раз, прирост производительности незначителен, но затраты на обслуживание высоки.
Выучите Java String Intern - раз и навсегда
Строки в Java являются неизменными объектами по своему дизайну. Поэтому два строковых объекта даже с одинаковым значением будут разными объектами по умолчанию. Однако, если мы хотим сэкономить память, мы могли бы указать на использование той же памяти с помощью концепции, называемой string intern.
Приведенные ниже правила помогут вам понять концепцию в четких терминах:
- Класс String поддерживает внутренний пул, который изначально пуст. Этот пул должен содержать строковые объекты только с уникальными значениями.
- Все строковые литералы, имеющие одинаковое значение, должны рассматриваться как один и тот же объект памяти, поскольку в противном случае они не имеют понятия различия. Следовательно, все такие литералы с одинаковым значением сделают одну запись в пуле и будут ссылаться на одну и ту же ячейку памяти.
- Объединение двух или более литералов также является литералом. (Поэтому правило № 2 будет применимо к ним)
- Каждая строка, созданная как объект (т. Е. Любым другим методом, кроме как в качестве литерала), будет иметь разные области памяти и не будет делать никаких записей в пуле интернирования.
- Объединение литералов с не-литералами сделает не-литерал. Таким образом, результирующий объект будет иметь новое место в памяти и НЕ будет делать запись в пуле.
- Вызов метода intern для строкового объекта либо создает новый объект, который входит в intern-pool, либо возвращает существующий объект из пула с таким же значением. Вызов любого объекта, которого нет в пуле, НЕ перемещает объект в пул. Это скорее создает другой объект, который входит в пул.
Пример:
String s1=new String (“abc”);
String s2=new String (“abc”);
If (s1==s2) //would return false by rule #4
If (“abc” == “a”+”bc” ) //would return true by rules #2 and #3
If (“abc” == s1 ) //would return false by rules #1,2 and #4
If (“abc” == s1.intern() ) //would return true by rules #1,2,4 and #6
If ( s1 == s2.intern() ) //wound return false by rules #1,4, and #6
Примечание: мотивационные случаи для строкового интерна здесь не обсуждаются. Тем не менее, сохранение памяти, безусловно, будет одной из основных задач.
Строковые литералы и константы интернированы по умолчанию. То есть, "foo" == "foo"
(объявлено строковыми литералами), но new String("foo") != new String("foo")
,
Вы должны разобрать два периода времени, которые являются временем компиляции и временем выполнения. Например:
//example 1
"test" == "test" // --> true
"test" == "te" + "st" // --> true
//example 2
"test" == "!test".substring(1) // --> false
"test" == "!test".substring(1).intern() // --> true
с одной стороны, в примере 1 мы находим, что все результаты возвращают true, потому что во время компиляции jvm поместит "test" в пул литеральных строк, если jvm find "test" существует, тогда он будет использовать существующий, в примере 1 все строки "test" указывают на один и тот же адрес памяти, поэтому в примере 1 будет возвращаться true. с другой стороны, в примере 2 метод substring() выполняется во время выполнения, в случае "test" == "!test".substring(1), пул создаст два строковых объекта " test "и"!test", поэтому они являются разными ссылочными объектами, поэтому в этом случае будет возвращено значение false, в случае"test" == "!test".substring(1).intern(), метод intern () поместит "!test".substring(1)"в пул литеральных строк, поэтому в этом случае они являются одинаковыми ссылочными объектами, поэтому вернут true.
http://en.wikipedia.org/wiki/String_interning
Интернирование строк - это метод хранения только одной копии каждого отдельного строкового значения, которое должно быть неизменным. Стажировка строк делает некоторые задачи обработки строк более экономичными по времени или пространству за счет того, что при создании или интернировании строки требуется больше времени. Отдельные значения хранятся в строковом внутреннем пуле.
String s1 = "Anish";
String s2 = "Anish";
String s3 = new String("Anish");
/*
* When the intern method is invoked, if the pool already contains a
* string equal to this String object as determined by the
* method, then the string from the pool is
* returned. Otherwise, this String object is added to the
* pool and a reference to this String object is returned.
*/
String s4 = new String("Anish").intern();
if (s1 == s2) {
System.out.println("s1 and s2 are same");
}
if (s1 == s3) {
System.out.println("s1 and s3 are same");
}
if (s1 == s4) {
System.out.println("s1 and s4 are same");
}
ВЫХОД
s1 and s2 are same
s1 and s4 are same
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = "test";
String s2 = new String("test");
System.out.println(s1==s2); //false
System.out.println(s1==s2.intern()); //true --> because this time compiler is checking from string constant pool.
}
String p1 = "example";
String p2 = "example";
String p3 = "example".intern();
String p4 = p2.intern();
String p5 = new String(p3);
String p6 = new String("example");
String p7 = p6.intern();
if (p1 == p2)
System.out.println("p1 and p2 are the same");
if (p1 == p3)
System.out.println("p1 and p3 are the same");
if (p1 == p4)
System.out.println("p1 and p4 are the same");
if (p1 == p5)
System.out.println("p1 and p5 are the same");
if (p1 == p6)
System.out.println("p1 and p6 are the same");
if (p1 == p6.intern())
System.out.println("p1 and p6 are the same when intern is used");
if (p1 == p7)
System.out.println("p1 and p7 are the same");
Когда две строки создаются независимо, intern()
позволяет сравнивать их, а также помогает в создании ссылки в пуле строк, если ссылка не существовала ранее.
Когда вы используете String s = new String(hi)
Java создает новый экземпляр строки, но когда вы используете String s = "hi"
Ява проверяет, есть ли в коде слово "hi" или нет, и если оно существует, оно просто возвращает ссылку.
Поскольку сравнение строк основано на ссылке, intern()
помогает в создании ссылки и позволяет сравнивать содержимое строк.
Когда вы используете intern()
в коде он очищает пространство, используемое строкой, ссылающейся на тот же объект, и просто возвращает ссылку на уже существующий тот же объект в памяти.
Но в случае p5, когда вы используете:
String p5 = new String(p3);
Только содержимое p3 копируется и p5 создается заново. Так что это не интернировано.
Таким образом, результат будет:
p1 and p2 are the same
p1 and p3 are the same
p1 and p4 are the same
p1 and p6 are the same when intern is used
p1 and p7 are the same
Interned Strings избегают дублирования строк. Interning экономит оперативную память за счет увеличения процессорного времени для обнаружения и замены дублирующихся строк. Существует только одна копия каждой строки, которая была интернирована, независимо от того, сколько ссылок на нее указывают. Поскольку строки являются неизменяемыми, если два разных метода случайно используют одну и ту же строку, они могут совместно использовать копию одной строки. Процесс преобразования дублированных строк в разделяемые называется interning.String.intern() дает вам адрес канонического мастера String. Вы можете сравнить интернированные строки с простым == (который сравнивает указатели) вместо равных, которые сравнивают символы строки по одному. Поскольку строки являются неизменяемыми, внутренний процесс может дополнительно экономить пространство, например, не создавая отдельный строковый литерал для "pot", если он существует в качестве подстроки какого-либо другого литерала, такого как "бегемот".
Чтобы увидеть больше http://mindprod.com/jgloss/interned.html
Interned Strings избегают дублирования строк. Interning экономит оперативную память за счет увеличения процессорного времени для обнаружения и замены дублирующихся строк. Существует только одна копия каждой строки, которая была интернирована, независимо от того, сколько ссылок на нее указывают.
Хотя Java по умолчанию автоматически интернирует все строки, помните, что нам нужно интернировать строки только тогда, когда они не являются константами, и мы хотим иметь возможность быстро сравнить их с другими интернированными строками. Метод intern() должен использоваться в строках, созданных с помощью new String(), чтобы сравнивать их с помощью оператора ==.
открытый класс TestString {
public static void main(String[] args) {
String s1 = "Test";
String s2 = "Test";
String s3 = new String("Test");
final String s4 = s3.intern();
System.out.println(s1 == s2);
System.out.println(s2 == s3);
System.out.println(s3 == s4);
System.out.println(s1 == s3);
System.out.println(s1 == s4);
System.out.println(s1.equals(s2));
System.out.println(s2.equals(s3));
System.out.println(s3.equals(s4));
System.out.println(s1.equals(s4));
System.out.println(s1.equals(s3));
}
}
//Выход
правда
ложный
ложный
ложный
правда
правда
правда
правда
правда
правда
Используя ссылку на объект кучи, если мы хотим получить соответствующую ссылку на объект пула строковых констант, мы должны использовать intern()
String s1 = new String("Rakesh");
String s2 = s1.intern();
String s3 = "Rakesh";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
Шаг 1. Объект с данными "Rakesh" создается в пуле констант кучи и строк. Также s1 всегда указывает на объект кучи.
Шаг 2. Используя ссылку на объект кучи s1, мы пытаемся получить соответствующую ссылку на объект пула строковых констант s2, используя intern()
Шаг 3. Намеренное создание объекта с данными 'Rakesh' в пуле строковых констант, на который ссылается имя s3
Как оператор "==", предназначенный для сравнения ссылок.
Получение false для s1==s2
Получение истины для s2==s3
Надеюсь на эту помощь!!
Как вы сказали, эта строка intern()
Сначала метод найдет из пула String, если найдет, то вернет объект, который на него указывает, или добавит новую строку в пул.
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hello".intern();
String s4 = new String("Hello");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//true
System.out.println(s1 == s4.intern());//true
s1
а также s2
два объекта, указывающие на пул строк "Hello" и использующие "Hello".intern()
найдет что s1
а также s2
, Так "s1 == s3"
возвращает истину, а также s3.intern()
,
Метод string intern() используется для создания точной копии строкового объекта кучи в пуле строковых констант. Строковые объекты в пуле строковых констант автоматически интернируются, а строковые объекты в куче - нет. Основное использование создания интернов - это сохранение места в памяти и более быстрое сравнение строковых объектов.
Источник: Что такое строка интерна в Java?
Почему строковые литералы нельзя использовать в местах, где требуется использовать intern? Использование строкового литерала по умолчанию будет использовать существующие строковые литералы. Так почему же нам нужно создать новую строку ("что-то).intern() вместо того, чтобы просто назначать" что-то "?