Когда мы должны использовать метод интерна String для литералов String - PullRequest
178 голосов
/ 06 декабря 2009

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

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

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 ]

221 голосов
/ 06 декабря 2009

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 "Строковое равенство и интернирование" для получения дополнительной информации.

19 голосов
/ 06 декабря 2009

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

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

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

14 голосов
/ 06 декабря 2009

Я хочу добавить свои 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, не вдаваясь в подробности, что он ожидает массив интернированных строк.

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

Теперь у вас есть ошибка, которую может быть очень трудно найти, потому что в большинстве случаев массив содержит литеральные значения, а иногда используется не-литеральная строка. Если бы вместо 1023 использовалось equals, то hasReferenceVal продолжало бы работать. Еще раз, прирост производительности незначителен, но затраты на обслуживание высоки.

11 голосов
/ 06 декабря 2009

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

6 голосов
/ 01 апреля 2017

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

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

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

  1. Класс String поддерживает внутренний пул, который изначально пуст. Этот пул должен содержать строковые объекты только с уникальными значениями.
  2. Все строковые литералы, имеющие одинаковое значение, должны рассматриваться как один и тот же объект памяти, поскольку в противном случае они не имеют понятия различия. Следовательно, все такие литералы с одинаковым значением сделают одну запись в пуле и будут ссылаться на одну и ту же ячейку памяти.
  3. Объединение двух или более литералов также является литералом. (Поэтому правило № 2 будет применимо к ним)
  4. Каждая строка, созданная как объект (т. Е. Любым другим методом, кроме как в качестве литерала), будет иметь разные области памяти и не будет вносить никаких записей в пул интерна *
  5. Конкатенация литералов с не-литералами сделает не-литерал. Таким образом, результирующий объект будет иметь новую ячейку памяти и НЕ будет делать запись в пуле.
  6. Вызывая метод 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

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

4 голосов
/ 01 сентября 2014

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

//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 найдет «test» существует, тогда он будет использовать существующий, в примере 1 все строки «test» указывают на один и тот же адрес памяти, поэтому в примере 1 будет возвращено значение true. с другой стороны, в примере 2 метод substring () выполняется во время выполнения, в случае "test" == "! test" .substring (1), пул создаст два строковых объекта, "test" и "! test", поэтому они являются разными ссылочными объектами, поэтому в этом случае будет возвращено значение false, в случае "test" == "! test" .substring (1) .intern () метод intern () будет поместите "! test" .substring (1) "в пул литеральных строк, так что в этом случае они будут одинаковыми ссылочными объектами, поэтому вернет true.

3 голосов
/ 12 сентября 2013

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

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

2 голосов
/ 17 февраля 2018
    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.
}
2 голосов
/ 12 июля 2016
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
2 голосов
/ 12 ноября 2013
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");
        }

OUTPUT

s1 and s2 are same
s1 and s4 are same
...