Java intern all strings

Все о String.intern()

Думаю, что многие Java-разработчики знают или хотя бы слышали о методе String.intern() . Но далеко не каждый использует его в своих приложениях или представляет в каких случаях он полезен и полезен ли вообще. Так было и со мной до тех пор пока я не столкнулся с этим методом в одном из проектов. В тот момент я захотел узнать смысл и особенности его использования и набрел на одну весьма интересную статью ведущего разработчика Yahoo! по имени Ethan Nicholas, переводом которой теперь хочу поделиться с той частью Хабра-сообщества, которая не безразлична к языку Java.

Тем, кто знает об этом методе лишь понаслышке, добро пожаловать под кат.

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

Сегодня я просматривал исходный код Xerces (XML-парсер, включенных в Java) и наткнулся на строку, которая меня очень удивила:

com.sun.org.apache.xerces.internal.impl.XMLScanner:395
protected final static String fVersionSymbol = «version».intern();

Далее я нашел еще несколько строк, определенных как эта, и каждая из них была интернирована. Так что же такое intern() ? Ну, как вы, несомненно, знаете, существует два различных способа для сравнения объектов в Java. Вы можете использовать оператор == , или же вы можете использовать метод equals() . Оператор == сравнивает ссылаются ли две ссылки на один и тот же объект, в то время как equals() сравнивает содержат ли два объекта одни и те же данные.

Читайте также:  Cookie and sessions in html

Одним из первых уроков, который вы усваиваете при изучении Java является то, что обычно для сравнения двух строк вы должны использовать equals() , а не == . Если сравнить, скажем, new String(«Hello») == new String(«Hello») , то в результате получится false , потому что это два разных экземпляра класса. Если же вы используете equals() , то получите true , как и ожидаете. К сожалению, equals() может оказаться довольно медленным, поскольку он выполняет посимвольное сравнение строк.

Т.к. оператор == проверяет идентичность (identity), все, что он должен сделать — это сравнить два указателя, и, очевидно, это будет гораздо быстрее, чем equals() . Так что если вы собираетесь сравнивать одни и те же строки многократно, вы можете получить значительное преимущество в производительности за счет использования проверки идентичности объектов вместо сравнения символов.

1) Создать множество (hash set) строк
2) Проверить, что строка (как последовательность символов), с которой вы имеете дело, уже в множестве
3) Если да, то использовать строку из множества
4) В противном случае, добавить эту строку в множество и затем использовать ее

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

К счастью Java уже включает в себя реализацию этого алгоритма. Это метод intern() в классе java.lang.String . Выражение new String(«Hello»).intern() == new String(«Hello»).intern() возвращает true , в то время как без использования intern() возвращается false .

Так почему же я так удивился, увидев
protected final static String fVersionSymbol = «version».intern();
в исходном коде Xerces? Очевидно, что эта строка будет использоваться для многократных сравнений. Имеет ли смысл интернировать ее?

Конечно, имеет. Вот почему Java уже это делает. Все строки-константы, которые встречаются в классе автоматически интернированы. Сюда входят как собственные константы (например, приведенная выше строка «version» ), так и другие строки, которые являются частью формата файла класса — имена классов, сигнатуры методов и так далее. Это распространяется даже на выражения: «Hel» + «lo» обрабатывается javac точно так же, как «Hello» , поэтому «Hel» + «lo» == «Hello» возвращает true .

Таким образом, результатом вызова intern() для строки-константы типа «version» , по определению, будет точно тот же объект, который вы объявили. Другими словами «version» == «version».intern() всегда истинно. Вам нужно интернировать строки тогда, когда они не являются константами, и вы хотите иметь возможность быстро сравнить их с другими интернированными строками.

Также при интернировании строк можно получить преимущество в использовании памяти, т.к. вы храните в ней лишь один экземпляр последовательности символов строки, независимо от того, сколько раз вы на эту последовательность ссылаетесь. Это основная причина того, почему строковые константы файла класса интернированы: подумайте о том, сколько классов ссылаются, например, на java.lang.Object . Имя класса java.lang.Object должно появиться в каждом из этих классов, но, благодаря магии intern() , оно появляется в памяти лишь в одном экземпляре.

Вывод? intern() является полезным методом и может сделать жизнь легче — но убедитесь, что вы используете его должным образом.

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

Большое спасибо хабраюзеру nolled, который пригласил меня в Хабрасообщество.

Update
Думаю, что следующая информация, которую я узнал из других источников будет здесь не лишней:

1. Пул строк хранится в области «Perm Gen», которая зарезервирована для non-user объектов JVM (классы и пр). Если этого не учитывать, вы можете неожиданно получить OutOfMemory Error.
2. Интернированные строки не хранятся вечно. Строки, на которых нет ссылок, также удаляются сборщиком мусора.
3. В большинстве случаев вы не получите существенного прироста производительности от использования intern() — если сравнение строк не является основной (или очень частой) операцией вашего приложения и сравниваемые строки разные по длине.

Источник

Java String intern() method explained with examples

Java String intern() method is used for getting the string from the memory if it is already present. This method ensures that all the same strings share the same memory. For example, creating a string “hello” 10 times using intern() method would ensure that there will be only one instance of “Hello” in the memory and all the 10 references point to the same instance.

A Simple Java String intern() method example

This example demonstrates the use of intern() method. This method searches the memory pool for the mentioned String, if the string is found then it returns the reference of it, else it allocates a new memory space for the string and assign a reference to it.

Java automatically interns String literals

When we create Strings using string literals instead of creating them using new keyword then the java automatically interns String them. Lets take an example to understand this:

public class Example < public static void main(String args[])< String str1 = "Hello"; //Java automatically interns this String str2 = "Hello"; //This is same as creating string using string literal String str3 = "Hello".intern(); //This will create a new instance of "Hello" in memory String str4 = new String("Hello"); if ( str1 == str2 )< System.out.println("String str1 and str2 are same"); >if ( str2 == str3 ) < System.out.println("String str2 and str3 are same" ); >if ( str1 == str4 ) < //This will not be printed as the condition is not true System.out.println("String str1 and str4 are same" ); >if ( str3 == str4 ) < System.out.println("String str3 and str4 are same" ); >> >
String str1 and str2 are same String str2 and str3 are same String str3 and str4 are same

References:

About the Author

I have 15 years of experience in the IT industry, working with renowned multinational corporations. Additionally, I have dedicated over a decade to teaching, allowing me to refine my skills in delivering information in a simple and easily understandable manner.

Источник

Оцените статью