Вопрос-ответ

When should we use intern method of String on String literals

Когда мы должны использовать внутренний метод String для строковых литералов

Согласно String#intern(), intern предполагается, что метод возвращает строку из пула строк, если строка найдена в пуле строк, в противном случае в пул строк будет добавлен новый объект string и будет возвращена ссылка на эту строку.

Итак, я попробовал это:

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 метод? Другими словами, когда мы должны использовать этот метод?

Переведено автоматически
Ответ 1

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

Во всех случаях, кроме s4 переменной, значение для которой было явно создано с помощью new оператора и где intern метод не использовался для его результата, это единственный неизменяемый экземпляр, который возвращается пулом строковых констант JVM.

Обратитесь к JavaTechniques "Равенство строк и интернирование" для получения дополнительной информации.

Ответ 2

В недавнем проекте были созданы некоторые огромные структуры данных с данными, которые считывались из базы данных (и, следовательно, не строковыми константами / литералами), но с огромным количеством дублирований. Это было банковское приложение, и такие вещи, как названия скромного набора (возможно, 100 или 200) корпораций, появлялись повсюду. Структуры данных уже были большими, и если бы все эти названия корпораций были уникальными объектами, они бы переполняли память. Вместо этого все структуры данных имели ссылки на одни и те же 100 или 200 строковых объектов, что экономило много места.

Еще одно небольшое преимущество интернированных строк заключается в том, что == может использоваться (успешно!) Для сравнения строк, если все задействованные строки гарантированно будут интернированы. Помимо более компактного синтаксиса, это также повышает производительность. Но как указывали другие, выполнение этого сопряжено с большим риском возникновения ошибок программирования, поэтому это следует делать только в качестве крайней меры.

Недостатком является то, что интернирование строки занимает больше времени, чем простое добавление ее в кучу, и что пространство для интернированных строк может быть ограничено, в зависимости от реализации Java. Это лучше всего делать, когда вы имеете дело с известным разумным количеством строк со многими дублированиями.

Ответ 3

Предпочитаем String.equals вместо this==object

Я хочу добавить свои 2 цента за использование == с внутренними строками.

Первое, что String.equals нужно сделать, это this==object. Смотрите Исходный код на OpenJDK.

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}

Итак, хотя есть некоторый незначительный прирост производительности (вы не вызываете метод), с точки зрения сопровождающего использование == - это кошмар, потому что некоторые интернированные строки имеют тенденцию становиться не интернированными.

Поэтому я предлагаю не полагаться на особый случай == для интернированных строк, но всегда использовать 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 общедоступным, не вдаваясь в подробности, что он ожидает массив интернированных строк.

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 все равно продолжал бы работать. Еще раз, прирост производительности незначителен, но затраты на обслуживание высоки.

Ответ 4

Изучите Java String Intern - раз и навсегда

Строки в java по своей конструкции являются неизменяемыми объектами. Следовательно, два строковых объекта даже с одинаковым значением по умолчанию будут разными объектами. Однако, если мы хотим сэкономить память, мы могли бы указать на использование той же памяти с помощью концепции, называемой string intern .

Приведенные ниже правила помогут вам понять концепцию в четких терминах:


  1. Класс String поддерживает внутренний пул, который изначально пуст. Этот пул должен гарантированно содержать строковые объекты только с уникальными значениями.

  2. Все строковые литералы, имеющие одинаковое значение, должны рассматриваться как один и тот же объект расположения в памяти, потому что в противном случае они не имеют понятия о различии. Следовательно, все такие литералы с одинаковым значением будут вносить единую запись во внутренний пул и будут ссылаться на одну и ту же ячейку памяти.

  3. Объединение двух или более литералов также является литералом. (Следовательно, для них будет применимо правило # 2)

  4. Каждая строка, созданная как объект (т. Е. Любым другим методом, кроме как как литерал), будет иметь разные ячейки памяти и не будет вносить никаких записей во внутренний пул

  5. Конкатенация литералов с нелитералами приведет к созданию нелитерала. Таким образом, результирующий объект будет иметь новую ячейку памяти и не будет вносить запись во внутренний пул.

  6. Вызов внутреннего метода для строкового объекта либо создает новый объект, который попадает во внутренний пул, либо возвращает существующий объект из пула, который имеет то же значение. Вызов любого объекта, которого нет во внутреннем пуле, НЕ перемещает объект в пул. Он скорее создает другой объект, который попадает в пул.

Пример:

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

Примечание: Мотивационные примеры для string intern здесь не обсуждаются. Однако экономия памяти определенно будет одной из основных целей.

java string