Что такое интернирование строк
Перейти к содержимому

Что такое интернирование строк

  • автор:

String. Intern(String) Метод

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

Извлекает системную ссылку на указанный объект String.

public: static System::String ^ Intern(System::String ^ str);
public static string Intern (string str);
static member Intern : string -> string
Public Shared Function Intern (str As String) As String
Параметры

Строка для поиска в пуле интернирования.

Возвращаемое значение

Системная ссылка на значение str , если оно уже интернировано; в противном случае возвращается новая ссылка на строку со значением str .

Исключения

str имеет значение null .

Примеры

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

// Sample for String::Intern(String) using namespace System; using namespace System::Text; int main() < String^ s1 = "MyTest"; String^ s2 = (gcnew StringBuilder)->Append( "My" )->Append( "Test" )->ToString(); String^ s3 = String::Intern( s2 ); Console::WriteLine( "s1 == ''", s1 ); Console::WriteLine( "s2 == ''", s2 ); Console::WriteLine( "s3 == ''", s3 ); Console::WriteLine( "Is s2 the same reference as s1?: ", s2 == s1 ); Console::WriteLine( "Is s3 the same reference as s1?: ", s3 == s1 ); > /* This example produces the following results: s1 == 'MyTest' s2 == 'MyTest' s3 == 'MyTest' Is s2 the same reference as s1?: False Is s3 the same reference as s1?: True */ 
// Sample for String.Intern(String) using System; using System.Text; class Sample < public static void Main() < string s1 = "MyTest"; string s2 = new StringBuilder().Append("My").Append("Test").ToString(); string s3 = String.Intern(s2); Console.WriteLine($"s1 == "); Console.WriteLine($"s2 == "); Console.WriteLine($"s3 == "); Console.WriteLine($"Is s2 the same reference as s1?: "); Console.WriteLine($"Is s3 the same reference as s1?: "); > > /* This example produces the following results: s1 == MyTest s2 == MyTest s3 == MyTest Is s2 the same reference as s1?: False Is s3 the same reference as s1?: True */ 
// Sample for String.Intern(String) open System open System.Text let s1 = "MyTest" let s2 = StringBuilder().Append("My").Append("Test").ToString() let s3 = String.Intern s2 printfn $"s1 = " printfn $"s2 = " printfn $"s3 = " printfn $"Is s2 the same reference as s1?: obj = s1 :> obj>" printfn $"Is s3 the same reference as s1?: obj = s1 :> obj>" (* This example produces the following results: s1 = MyTest s2 = MyTest s3 = MyTest Is s2 the same reference as s1?: False Is s3 the same reference as s1?: True *) 
Imports System.Text Class Sample Public Shared Sub Main() Dim s1 As String = "MyTest" Dim s2 As String = New StringBuilder().Append("My").Append("Test").ToString() Dim s3 As String = String.Intern(s2) Console.WriteLine($"s1 = ") Console.WriteLine($"s2 = ") Console.WriteLine($"s3 = ") Console.WriteLine($"Is s2 the same reference as s1?: ") Console.WriteLine($"Is s3 the same reference as s1?: ") End Sub End Class ' 's1 = MyTest 's2 = MyTest 's3 = MyTest 'Is s2 the same reference as s1?: False 'Is s3 the same reference as s1?: True ' 

Комментарии

Дополнительные сведения об этом API см. в разделе Дополнительные примечания API для String.Intern.

Что такое интернирование строк

Что выведет на консоль следующий код:

var s1 = string.Format("", "abc", "cba"); var s2 = "abc" + "cba"; var s3 = "abccba"; Console.WriteLine(s1 == s2); Console.WriteLine((object)s1==(object)s2); Console.WriteLine(s2==s3); Console.WriteLine((object)s2==(object)s3);
  1. true, false, true, true
  2. true, true, true, true
  3. true, false, true, false
  4. true, false, false, false

Вопрос 6

Какие результаты выведет следующий код:

internal class Program < private static Object syncObject = new Object(); private static void Write() < lock (syncObject) < Console.WriteLine("test"); >> static void Main(string[] args) < lock (syncObject) < Write(); >> >
  1. Выбросит исключение
  2. Напечатает слово «test»
  3. Произойдет взаимоблокировка (deadlock)
  4. Напечатает слово «test» бесконечное число раз

Вопрос 7

Даны следующие классы:

public class A < public virtual void Print1() < Console.Write("A"); >public void Print2() < Console.Write("A"); >> public class B: A < public override void Print1() < Console.Write("B"); >> public class C : B < new public void Print2() < Console.Write("C"); >>

К какому результату приведет выполнение следующего кода:

var c = new C(); A a = c; a.Print2(); a.Print1(); c.Print2();

Вопрос 8

Что будет выведено на консоль в результате выполнения следующего кода:

static IEnumerable Square(IEnumerable a) < foreach(var r in a) < Console.WriteLine(r * r); yield return r * r; >> class Wrap < private static int init = 0; public int Value < get < return ++init; >> > static void Main(string[] args) < var w = new Wrap(); var wraps = new Wrap[3]; for(int i=0; ivar values = wraps.Select(x => x.Value); var results = Square(values); int sum = 0; int count = 0; foreach(var r in results) < count++; sum += r; >Console.WriteLine("Count ", count); Console.WriteLine("Sum ", sum); Console.WriteLine("Count ", results.Count()); Console.WriteLine("Sum ", results.Sum()); >

Как работает интернирование строк

коллеги! хотела бы спросить вопросы, которые у меня возникли при более подробном изучении языка .NET и трудности касаются как минимум ссылочного типа string 1) Intern pool; (пулСтрок) Позволяет объединить строки с одинаковыми значениями в них в определенный пул памяти. В данном случае мы инициализировали две строки с одинаковыми значениями и они попали в один пул памяти. пример в моем понимании:

string a = "aaa"; string b = "aaa"; bool c = (object)a == (object) b; // И получаем true 

ОДНАКО код:

string a = "aaa"; string b = "aa"; b+="a"; bool c = (object)a == (object) b; // И получаем false 

Почему так? и как это работает? по сути мы получаем строку с одинаковыми значениями и все они должны ссылаться на один объект. почему мы по итогу получаем разные результаты? Я понимаю, что в данном случае мы сравниваем объекты, а не содержимое. Но почему они не объединились?

Отслеживать
28.7k 22 22 золотых знака 61 61 серебряный знак 141 141 бронзовый знак
задан 30 авг 2018 в 12:11
125 1 1 серебряный знак 5 5 бронзовых знаков

Поиск строки в пуле — это операция за которую на платить. Не все хотят это делать. Если Вы готовы это делать, то можете вызывать данные операции явно: string.IsInterned и string.Intern .

30 авг 2018 в 12:17
вы не могли бы пояснить что имеете ввиду?
30 авг 2018 в 12:18

Предположу, что сравниваются по хешу, который генерируется при создании объекта. Во втором примере у вас объект изменяется. Попробуйте добавить третий вариант с удалением части строки и сравнением. По идее должно быть true на выходе.

30 авг 2018 в 12:19

«почему они не объединились?» Потому что, в первом случае известно что строка одна на этапе компиляции. А во, втором уже на этапе выполнения программы

30 авг 2018 в 12:20
30 авг 2018 в 13:53

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

В первом вашем примере строки интернируются на этапе компиляции. Если посмотреть в утилите ILDASM, то в окне MetaInfo в разделе User Strings будет представлен всего один экземпляр:

70000001 : ( 3) L"aaa" 

Соответственно, обе переменные: и a , и b будут указывать на этот адрес.

Во втором примере в результате конкатенации тоже получается строка «aaa», но она не заносится по умолчанию в пул интернированных строк, потому что для этого нужны дополнительные проверки, то есть тратится время.

Во втором примере можно добавить строку в пул вручную:

string a = "aaa"; string b = "aa"; b += "a"; b = string.Intern(b); bool c = (object)a == (object)b; // True 

Ответ на вопрос из комментария:

Я не уверен, но, думаю, выгода в том, что после интернирования переменная b станет указывать на тот же участок памяти, что и a . После чего сборщик мусора сможет убрать другой экземпляр строки «aaa». Это почти бессмысленно для коротких строк, но может оказаться выгодно для длинных долгоживущих строк.

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

Отслеживать
ответ дан 30 авг 2018 в 12:23
Alexander Petrov Alexander Petrov
29.2k 5 5 золотых знаков 28 28 серебряных знаков 55 55 бронзовых знаков

это потрясающий ответ. они неизменяемы во время компиляции явно и , получается, при компиляции мы получаем совершенно иной объект ?

30 авг 2018 в 12:25

@Alexander Petrov, может лучше тогда объединить две строки в b = string.Intern(b+= «a»); ? Ну чтобы объект вообще не создавался.

30 авг 2018 в 12:27

@Kate — то ли я вас не понял, то ли вы что-то не совсем понимаете. Одинаковые строковые литералы на этапе компиляции будут представлены в одном экземпляре, и далее везде будут использованы ссылки на него. А в рантайме получается другой экземпляр. Естественно, у него другая ссылка.

30 авг 2018 в 12:30

@4per — думаю, это ничего не изменит. Новый объект строки все равно будет создан в результате конкатенации. Нужно смотреть IL-код (а мне лень. )

30 авг 2018 в 12:31

@4per — я не уверен, но, думаю, выгода в том, что после интернирования переменная b станет указывать на тот же участок памяти, что и a . После чего сборщик мусора сможет убрать другой экземпляр строки «aaa» . Это почти бессмысленно для коротких строк, но может оказаться выгодно для длинных долгоживущих строк.

30 авг 2018 в 12:35

Чтобы не гадать, глянем сразу IL-инструкции которые генерирует компилятор:

IL_0000: nop IL_0001: ldstr "aaa" IL_0006: stloc.0 // a IL_0007: ldstr "aaa" IL_000C: stloc.1 // b IL_000D: ldloc.0 // a IL_000E: ldloc.1 // b IL_000F: ceq IL_0011: stloc.2 // c IL_0012: ldstr "aaa" IL_0017: stloc.3 // a2 IL_0018: ldstr "aa" IL_001D: stloc.s 04 // b2 IL_001F: ldloc.s 04 // b2 IL_0021: ldstr "a" IL_0026: call System.String.Concat IL_002B: stloc.s 04 // b2 IL_002D: ldloc.3 // a2 IL_002E: ldloc.s 04 // b2 IL_0030: ceq IL_0032: stloc.s 05 // c2 IL_0034: ret 

Как видим в данном случае в первом блоке мы записали одну и ту же строку в a (IL_0006) и b (IL_000C), и в данном случае строка является интернированной, во втором же блоке только ‘a2′(IL_0017) является интернированной, b2 после конкатенации (IL_0026) и присваивания (IL_002B) таковой не является.

UPD: Следующий код также возвращает false :

Console.WriteLine(ReferenceEquals(b2, a2)); 

Метод ReferenceEquals определяет, совпадают ли указанные экземпляры Object . При сравнении строк. Если objA и objB являются строками, ReferenceEquals возвращает true если строки интернированы. Он не выполняет проверку на равенство значений. В следующем примере s1 и s2 равны, поскольку они являются двумя экземплярами одной интернированной строки. Тем не менее s3 и s4 не равны, поскольку несмотря на то, что они имеют идентичные строковые значения, эти строки не интернированы.

using System; public class Example < public static void Main() < String s1 = "String1"; String s2 = "String1"; Console.WriteLine("s1 = s2: ", Object.ReferenceEquals(s1, s2)); Console.WriteLine(" interned: ", s1, String.IsNullOrEmpty(String.IsInterned(s1)) ? "No" : "Yes"); String suffix = "A"; String s3 = "String" + suffix; String s4 = "String" + suffix; Console.WriteLine("s3 = s4: ", Object.ReferenceEquals(s3, s4)); Console.WriteLine(" interned: ", s3, String.IsNullOrEmpty(String.IsInterned(s3)) ? "No" : "Yes"); > > // The example displays the following output: // s1 = s2: True // String1 interned: Yes // s3 = s4: False // StringA interned: No 

P.s.: Метод также возвратит false при сравнении типов значений, если objA и objB являются типами значений, упакованы перед передачей в ReferenceEquals и представляют одно и то же значение.

int int1 = 3; Console.WriteLine(Object.ReferenceEquals(int1, int1)); //False Console.WriteLine(int1.GetType().IsValueType); //True 

Pascal4eg

Интернирование строк (string intern) — это процесс хранения одной и той же строки только в одном экземпляре в пуле строк (string pool) для оптимизации использования памяти и ускорения сравнения строк.

Вместо того чтобы создавать новый объект строки каждый раз, когда вы используете литерал строки (например, «Hello, World!»), Java проверяет, существует ли уже такая строка в пуле строк. Если она там есть, Java использует существующий экземпляр строки, иначе создает новый и добавляет его в пул строк.

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *