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

Ответ 1

Java автоматически ставит строковые литералы. Это означает, что во многих случаях оператор == работает для строк так же, как и для int или других примитивных значений.

Поскольку интернирование является автоматическим для строковых литералов, метод 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 Equality and Interning" .

Ответ 2

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

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

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

Ответ 3

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

Первое, что делает String.equals, - это this==object.

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

Поэтому я предлагаю не полагаться на специальный случай == для интернированных строк, но всегда используйте equals, как предполагал Гослинг.

EDIT: интернированный становится неинтерминированным:

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

Строковые литералы и константы интернированы по умолчанию. То есть "foo" == "foo" (объявлено литералами String), но new String("foo") != new String("foo").

Ответ 5

Изучите Java String Intern - один раз для всех

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

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

  • Класс String поддерживает базовый пул, который изначально пуст. Этот пул должен гарантировать, что он содержит строковые объекты с уникальными значениями.
  • Все строковые литералы, имеющие одинаковое значение, должны считаться одним и тем же объектом памяти, поскольку в противном случае они не имеют понятия различия. Таким образом, все такие литералы с одинаковым значением сделают одну запись в международном пуле и будут ссылаться на то же место в памяти.
  • Конкатенация двух или более литералов также является буквальной. (Поэтому для них применимо правило № 2)
  • Каждая строка, созданная как объект (т.е. любым другим методом, кроме как литерала), будет иметь разные ячейки памяти и не будет вносить никакой записи в международный пул
  • Конкатенация литералов с помощью нелитералов сделает нелитерал. Таким образом, результирующий объект будет иметь новую ячейку памяти и НЕ будет делать запись во внутреннем пуле.
  • Вызов метода intern для строкового объекта либо создает новый объект, который входит в внутренний пул, либо возвращает существующий объект из пула с одинаковым значением. Вызов на любой объект, который не находится в промежуточном пуле, НЕ перемещает объект в пул. Это скорее создает другой объект, который входит в пул.

Пример:

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

Ответ 6

вы должны выделить два периода времени, которые являются временем компиляции и временем выполнения. Например:

//example 1 
"test" == "test" // --> true 
"test" == "te" + "st" // --> true

//example 2 
"test" == "!test".substring(1) // --> false
"test" == "!test".substring(1).intern() // --> true

с одной стороны, в примере 1 мы находим, что все результаты верны, потому что во время компиляции jvm поместит "тест" в пул литеральных строк, если jvm найдет "тест", существует, то он будет использовать существующий, в примере 1, "тестовые" строки все указывают на один и тот же адрес памяти, поэтому пример 1 вернет true. с другой стороны, в примере 2 метод substring() выполняется во время выполнения, в случае "test" == "! test" .substring(1) пул создаст два строковых объекта, "test" и "! test" , поэтому они являются разными ссылочными объектами, поэтому этот случай вернет false, в случае "test" == "! test" .substring(1).intern() метод intern() будет поставьте "!! test".substring(1) "в пул литералов, так что в этом случае они являются одинаковыми ссылочными объектами, поэтому вернет true.

Ответ 7

http://en.wikipedia.org/wiki/String_interning

string interning - это метод хранения только одной копии каждого отдельного строкового значения, которое должно быть неизменным. Внутренние строки делают некоторые задачи обработки строк более экономными по времени или пространству за счет того, что требуется больше времени при создании или интернировании строки. Разные значения хранятся в пуловом пуле строк.

Ответ 8

Интернированные строки избегают дублирования строк. Interning сохраняет RAM за счет большего времени процессора для обнаружения и замены повторяющихся строк. Существует только одна копия каждой строки, которая была интернирована, независимо от того, сколько ссылок указывает на нее. Поскольку строки являются неизменяемыми, если два разных метода случайно используют одну и ту же строку, они могут совместно использовать копию той же строки. Процесс преобразования дублированных строк в общие называется interning.String.intern() дает вам адрес канонического мастера String. Вы можете сравнить интернированные строки с простым == (который сравнивает указатели) вместо equals, который сравнивает символы String один за другим. Поскольку строки являются неизменными, процесс intern может свободно экономить место, например, не создавая отдельный строковый литерал для "pot", если он существует как подстрока другого литерала, такого как "hippopotamus".

Чтобы увидеть больше http://mindprod.com/jgloss/interned.html

Ответ 9

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

Ответ 10

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", java проверяет, есть ли в коде экземпляр слова "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

Ответ 11

    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.
}

Ответ 12

Почему нельзя использовать строковые литералы в тех местах, где стажер старается использовать? Строковое использование литерала по умолчанию будет повторно использовать существующие строковые литералы. Итак, зачем нам создавать новую String ( "что-то" ).intern() вместо того, чтобы просто присваивать "что-то"?

Ответ 13

Метод string intern() используется для создания точной копии объекта строки кучи в пуле строковых констант. Строковые объекты в пуле строковых констант автоматически интернированы, но строковые объекты в куче - нет. Основное использование создания стажеров - это сохранение пространства памяти и более быстрое сравнение строковых объектов.

Источник: Что такое string intern в java?

Ответ 14

Как вы сказали, этот метод 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" возвращает true, а также s3.intern().

Ответ 15

открытый класс HelloWorld {

 public static void main(String []args){
     String s1=new String("hello");
     String s2=s1.concat("world");
     String s3=s2.intern();
     System.out.println(s2==s3);

 }

}

возвращает false до версии Java 1.5 и возвращает true из версии Java 1.6. Есть ли серьезные изменения в интернете с версии 1.6?