Коли нам слід використовувати інтерн-метод String on String Literals


187

Відповідно до String # intern () , internметод повинен повернути String з пулу String, якщо String знайдений у пулі String, інакше в рядок String буде доданий новий об'єкт рядка та повернеться посилання на цю 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метод? Іншими словами, коли нам слід використовувати цей метод?


14
Javadoc, з яким ви зв’язувались, також заявляє, що "всі буквені рядки та постійні вирази, що оцінюються за рядками, інтерновані".
Jorn


1
не точний дублікат ..
Божо

1
@Jorn: саме так. То чому ми маємо internяк публічний метод. Чи не мав би ми бути internприватним методом, щоб ніхто не мав до нього доступу. Або є якась мета цього методу?
Ракеш Джуял

2
@RakeshJuyal: Метод стажування визначається на тип рядка, який може бути рядковим літералом або змінним. Як би ви інтернували змінну, якби метод був приватним?
bobbyalex

Відповіді:


230

Java автоматично інтернується лінійних рядків. Це означає, що в багатьох випадках оператор == працює для Strings так само, як і для ints чи інших примітивних значень.

Оскільки інтернування є автоматичним для рядкових літералів, intern()метод повинен використовуватися для Strings, створених за допомогою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 "Рівність струнності та інтернування" .


Я припускаю, що Java автоматично інтернірує рядки String для оптимізації. Це можна зробити безпечно лише тому, що струни незмінні, правда?
стиле

Новачок у Java (я із світу C # .NET), і я іноді бачу застарілий проект "" .intern (), тому якщо я правильно розумію, що це "дурниця" також для порожніх рядків.
hfrmobile

4
@ Мігель Приємне пояснення, моє запитання полягає в тому, як об’єкт може бути створений тут у вашому прикладі. Ось Моє припущення: String s1 = "Rakesh"; перший OB1 String s4 = new String("Rakesh");Другий OB2 Отже, решта (s2, s3, s5) посилаються на той самий об'єкт (OB1), створений у 'string Pool'. Тож чи можу я сказати, що .intern()метод, який використовується для запобігання створенню нового об'єкта, якщо такий самий рядок доступний у string poolIf моє припущення неправильне, тому дайте мені вказівку.
HybrisHelp

1
Посилання на JavaTechniques розірвано
SJuan76


20

У недавньому проекті було створено кілька величезних структур даних із даними, які були прочитані з бази даних (а отже, не String-константи / літерали), але з величезною кількістю дублювання. Це була банківська програма, і такі речі, як назви скромних наборів корпорацій (можливо, 100 чи 200), з'являлися всюди. Структури даних вже були великими, і якби всі ці назви корпоративу були унікальними об'єктами, вони б переповнили пам'ять. Натомість у всіх структурах даних були посилання на ті самі 100 або 200 String-об'єктів, що економило багато місця.

Ще одна невелика перевага інтернованих рядків полягає в тому, що ==їх можна (успішно використовувати) для порівняння струн, якщо гарантовано інтерновані всі залучені рядки. Крім ситного синтаксису, це також підвищення продуктивності. Але, як зазначають інші, це робить великим ризиком введення помилок програмування, тому це слід робити лише як відчайдушний захід в крайньому випадку.

Мінус полягає в тому, що інтернування струни займає більше часу, ніж просто кидання її в купу, і що місце для інтернованих рядків може бути обмеженим, залежно від реалізації Java. Це найкраще робити, коли ви маєте справу з відомою розумною кількістю рядків з багатьма дублюваннями.


@ The downside is that interning a String takes more time than simply throwing it on the heap, and that the space for interned Strings may be limitedнавіть якщо ви не використовуєте інтерн-метод для String-константи, він буде інтернований автоматично.
Ракеш Джуял

2
@ Ракеш: Зазвичай у будь-якому класі звичайних струнних констант не так багато, тому це не проблема простору / часу з константами.
Девід Родрігес - дрибес

Так, коментар Ракеша не застосовується, оскільки інтернування інтерфейсів Strings виконується лише (явно) із рядками, які якось "генеруються", будь то внутрішніми маніпуляціями або вилученням із бази даних чи подібних. З константами у нас немає вибору.
Карл Смотрич

2
+1. Я думаю, що це хороший приклад, коли інтернування має сенс. Я не погоджуюся щодо ==струн.
Олександр Погребняк

1
Починаючи з Java 7, "String pool" реалізований у купі простору, тому він отримує всі переваги для зберігання інтернів, збирання сміття і розмір його не обмежується, його можна збільшити до розміру купи (вам ніколи не знадобиться стільки пам'ять для струн)
Аніл Уттані

15

Я хочу додати свої 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використовувались, все одно продовжували б працювати. Знову ж таки, підвищення продуктивності незначно, але вартість обслуговування велика.


"деякі інтерновані рядки мають тенденцію стати не інтернованими." вау, це було б ... дивно. Чи можете ви навести посилання, будь ласка?
Карл Смотрич

2
Гаразд, я подумав, що ти маєш на увазі Струни, які насправді вигулюють з басейну для стажування та над купою завдяки магії в JVM. Що ви говорите, це те, що == робить певні класи помилок програміста більш імовірними.
Карл Смотрич

"Тому я пропоную не покладатися на особливий випадок == для інтернованих рядків, а завжди використовувати дорівнює рівню, як задумав Гослінг." Чи є у вас пряма цитата чи коментар від Гослінга, що це говорить? Якщо це так, то чому він навіть клопочеться ставити інтерн () та використання == у мові?

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

12

Строкові літерали та константи за замовчуванням інтернуються Тобто "foo" == "foo"(оголошено рядковими літералами), але new String("foo") != new String("foo").


4
Отже, питання в тому, коли нам користуватися intern,
Ракеш Джуял

на що вказували stackoverflow.com/questions/1833581/when-to-use-intern та ряд інших питань, деякі з них із вчорашнього дня.
Божо

Повідомте мене String literals and constants are interned by default, чи правильно я розумію це твердження: new String("foo")-> Тут один String буквальний "foo" створюється в String пулі і один в купі, тому загалом створено 2 об'єкти.
dkb

8

Дізнайтеся 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

Примітка: мотиваційні випадки для рядкового інтерна тут не розглядаються. Однак збереження пам'яті, безумовно, буде однією з головних цілей.


Дякую за # 3, я не знаю :)
kaay

4

Ви повинні виділити два періоди, які складають час компіляції та час виконання. Наприклад:

//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 поверне справжній. з іншого боку, у прикладі 2 метод виконання substring () виконується під час виконання, у випадку "test" == "! test" .supstring (1), пул створить два рядкових об'єкта, " test "і"! test ", тож вони є різними еталонними об'єктами, тому цей випадок поверне помилковим, у випадку" test "=="! test ".substring (1) .intern (), методом intern ( ) поставить ""! test ".substring (1)" в пул буквальних рядків,


3

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

інтернування інтерфейсу - це метод зберігання лише однієї копії кожного окремого значення рядка, який повинен бути незмінним. Інтернування рядків робить деякі завдання обробці рядків більш ефективними в часі або просторі за рахунок необхідності більше часу при створенні або інтернуванні рядка. Відмінні значення зберігаються в пулу інтернів.


2

Інтерновані струни уникають повторюваних рядків. Інтернування економить оперативну пам’ять за рахунок більшого часу процесора для виявлення та заміни дублікатів Strings. Існує лише одна копія кожного рядка, який був інтернований, незалежно від кількості посилань на нього. Оскільки Strings незмінні, якщо два різні методи випадково використовують одну і ту ж String, вони можуть ділитися копією тієї ж String. Процес перетворення дублюваних рядків у загальні називається interning.String.intern () дає вам адресу канонічного майстра String. Ви можете порівняти інтерновані рядки з простими == (що порівнює вказівники) замість рівнихякий порівнює символів рядка один за одним. Оскільки рядки незмінні, процес стажування вільний додатково економити простір, наприклад, не створюючи окремого рядка-літералу для "горщика", коли він існує як підрядка якогось іншого літералу, такого як "гіпопотам".

Щоб побачити більше http://mindprod.com/jgloss/interned.html


2
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

2
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 перевіряє, чи є в коді екземпляр слова "привіт" чи ні, і якщо він існує, він просто повертає посилання.

Оскільки порівняння рядків засноване на посиланні, 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
    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.
}

1

Метод string intern () використовується для створення точної копії об'єкта рядка купи в постійному пулі рядків. Об'єкти рядків у постійному пулі рядків автоматично інтернуються, але об’єкти рядків у купі немає. Основне використання створення інтернів - це економія простору пам'яті та швидше порівняння струнних об'єктів.

Джерело: Що таке струнний стажер у Java?


1

Як ви вже говорили, цей рядовий 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об'єкти, що вказують на рядок String "Hello", і за допомогою "Hello".intern()знайдемо те s1і s2. Так "s1 == s3"повертається вірно, як і до s3.intern().


Це насправді не дає багато нової інформації. Відповідь вже є.
Олександр

0

Використовуючи посилання на об'єкт heap, якщо ми хочемо отримати відповідну рядкову посилання на постійний об'єкт пулу , тоді нам слід перейти до 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: Використовуючи посилання на об'єкт heap об'єкта, ми намагаємося отримати відповідний рядковий об'єкт постійного пулу referenc s2, використовуючи intern ()

Крок 3: Навмисне створення об'єкта з даними "Ракеш" у рядковому постійному пулі, на яке посилається ім'я s3

Оскільки оператор "==" призначений для порівняння порівняння.

Отримання помилки для s1 == s2

Визначення істини для s2 == s3

Сподіваюся, що це допоможе !!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.