String değişmezleri için String öğesinin intern yöntemini ne zaman kullanmalıyız?


187

Göre Dize # stajyer () , internyöntem Dize havuzunda bulunursa aksi takdirde yeni dize nesne dize havuzda eklenecek ve bu dize referans döndürülür, dize havuzundan Dize dönmek gerekiyordu.

Bu yüzden denedim:

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

Bunun s1 and s3 are sames3 stajyer olduğu ve s1 and s2 are sameyazdırılmayacağı için yazdırılmasını bekliyordum. Ancak sonuç: her iki satır da yazdırılır. Bu, varsayılan olarak, String sabitleri stajyedir. Ama eğer öyleyse, neden internyönteme ihtiyacımız var ? Başka bir deyişle, bu yöntemi ne zaman kullanmalıyız?


14
Bağladığınız Javadoc ayrıca "Tüm gerçek dizeler ve dize değerli sabit ifadeler de stajyedir."
Jorn


1
tam bir kopya değil ..
Bozho

1
@Jorn: doğru. Öyleyse neden interngenel yöntem olarak var? internÖzel yöntem olarak olmamalı mıyız , böylece kimse buna erişemezdi. Yoksa bu yöntemin herhangi bir amacı var mı?
Rakesh Juyal

2
@RakeshJuyal: Staj yöntemi, dize değişmezleri veya değişkenleri olabilen bir dize türünde tanımlanır. Yöntem özel olsaydı bir değişkeni nasıl staj yaparsınız?
bobbyalex

Yanıtlar:


230

Java, String değişmezlerini otomatik olarak staj eder. Bu, birçok durumda == operatörünün Dizeler için ints veya diğer ilkel değerlerde olduğu gibi çalıştığı anlamına gelir.

Staj, String değişmezleri için otomatik olduğundan, intern()yöntem, aşağıdakilerle oluşturulan Dizelerde kullanılmalıdır:new String()

Örneğinizi kullanma:

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

dönecek:

s1 and s2 are same
s1 and s3 are same
s1 and s5 are same

s4Değişken dışındaki tüm durumlarda , değeri newişleç kullanılarak açıkça oluşturulmuş ve internyöntemin sonuçta kullanılmadığı durumlarda, JVM'nin dize sabit havuzuna döndürülen tek bir değişmez örnektir .

Daha fazla bilgi için JavaTechniques "Dize Eşitliği ve Stajyerlik" bölümüne bakınız .


Java'nın optimizasyon amacıyla otomatik olarak String değişmezlerini staj ettiğini varsayıyorum. Bunu sadece Dizeler değişmez olduğu için güvenle yapabilir, doğru mu?
styfle

Java'da yeni (C # .NET dünyasındanım) ve bazen Java eski projesinde "" .intern () görüyorum, bu yüzden doğru anlıyorsam bunun boş dizeler için de "saçmalık" olduğunu görüyorum.
hfrmobile

4
@Miguel Güzel bir açıklama, Sorum, burada örnekte nesnenin nasıl yaratılabileceğidir. İşte Benim Varsayım geçerli: String s1 = "Rakesh"; İlk OB1'i String s4 = new String("Rakesh");i söyleyebiliriz 'dizesi Havuzu' So oluşturulan (s2, s3, s5) referans aynı nesne (OB1) İkinci OB2 Yani gerisi .intern()önlenebilmesi için alınması kullanılan yöntem yeni bir nesne oluşturmak için eğer mevcut aynı dize string poolIf benim varsayım yanlış yani bana yön ver.
HybrisHelp

1
JavaTechniques bağlantısı kopuk
SJuan76


20

Son zamanlardaki bir projede, bazı büyük veri yapıları bir veritabanından okunan verilerle (ve dolayısıyla String sabitleri / değişmezleri değil) ancak büyük miktarda çoğaltma ile kuruldu. Bir bankacılık uygulamasıydı ve mütevazı bir setin (belki 100 veya 200) şirketlerin isimleri gibi şeyler her yerde ortaya çıktı. Veri yapıları zaten büyüktü ve tüm bu corp isimleri benzersiz nesneler olsaydı, hafızayı aşmış olurdu. Bunun yerine, tüm veri yapılarının aynı 100 veya 200 String nesnesine referansları vardı, böylece çok fazla alan tasarrufu sağladı.

Stajlı Dizelerin bir diğer küçük avantajı, ==tüm ilgili dizelerin stajyer olması garanti edilirse Dizeleri karşılaştırmak için kullanılabilmesidir (başarıyla!). Daha zayıf sözdiziminden başka, bu aynı zamanda bir performans geliştirmesidir. Ancak diğerlerinin de belirttiği gibi, bu limanları yapmak, programlama hatalarını getirme konusunda büyük bir risktir, bu yüzden bu sadece son çare için farklı bir önlem olarak yapılmalıdır.

Dezavantajı, bir Dize'nin interning'in onu yığın üzerine atmaktan daha fazla zaman alması ve interned Dizeler için alanın Java uygulamasına bağlı olarak sınırlı olabilmesidir. En iyi şekilde, çoğaltılmış birçok makul sayıda Dizeyle uğraşırken yapılır.


@ 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 limitedString sabiti için intern yöntemini kullanmasanız bile otomatik olarak interned olur.
Rakesh Juyal

2
@Rakesh: Belirli bir sınıfta genellikle pek çok String sabiti yoktur, bu nedenle sabitler ile boşluk / zaman sorunu değildir.
David Rodríguez - dribeas

Evet, Rakesh'in yorumu geçerli değildir çünkü stajyer Dizeler yalnızca (açıkça) içsel manipülasyon veya bir veritabanından veya benzeri bir yoldan alındığında bir şekilde "üretilen" Dizelerle yapılır. Sabitler ile başka seçeneğimiz yok.
Carl Smotricz

2
+1. Bence bu stajyerliğin ne zaman mantıklı olduğu konusunda iyi bir örnek. Ben de ==dizeleri için kabul etmiyorum .
Alexander Pogrebnyak

1
Java 7'den itibaren "String pool" yığın alanına uygulanır, böylece stajyerlerin depolanmasının tüm avantajlarını alır, çöp toplama ve boyutu sınırlandırılmaz, yığın boyutuna kadar artırılabilir. dizeleri için bellek)
Anıl Uttani

15

==İnterned dizeleri ile kullanarak 2 sent eklemek istiyorum .

Ilk şey String.equalsyapmasıdır this==object.

Bazı ufak bir performans kazancı olmasına rağmen (bir yöntem çağırmıyorsunuz), ==bakıcı kullanımı açısından bir kabus, çünkü bazı stajyerlerin interned olma eğilimi var.

Bu nedenle ==, stajyer dizeler için özel bir duruma güvenmemeyi , ancak her zaman equalsGosling'in amaçladığı gibi kullanmasını öneriyorum .

EDIT: stajyer olma stajyer olma:

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

Sürüm 2.0'da sürdürücü hasReferenceVal, bir dizi staj dizisi beklediğinden çok ayrıntıya girmeden herkese açık hale getirmeye karar verdi .

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

Şimdi bir hata var, bulmak çok zor olabilir, çünkü çoğu durumda dizi değişmez değerler içerir ve bazen değişmez bir dize kullanılır. Eğer equalsyerine kullanılmıştır ==sonra hasReferenceValhala sahip çalışmalarına devam edecekti. Bir kez daha, performans kazancı miniktir, ancak bakım maliyeti yüksektir.


"bazı stajyerlerin stajyer olma eğilimi vardır." vay, bu ... garip olurdu. Bir referans verebilir misiniz, lütfen?
Carl Smotricz

2
Tamam, JVM'deki sihir sayesinde stajyer havuzundan ve yığın üzerine dolaşan Dizeler'den bahsettiğinizi düşündüm. Söylediğiniz şey, == belirli programcı hataları sınıflarını daha olası kılıyor.
Carl Smotricz

"Bu yüzden, sabit dizeler için == özel durumuna güvenmemeyi öneririm, ancak Gosling'in amaçladığı gibi her zaman eşittir." Bunu belirten doğrudan bir teklifiniz veya yorumunuz var mı? Bu durumda neden stajyer () koymayı ve dilde == kullanımını bile rahatsız etti?

1
stajyer, her iki dizge de stajyer olsa bile, doğrudan karşılaştırma (==) için iyi değildir. kullanılan toplam belleği azaltmak harika: aynı dize 1'den fazla yerde kullanıldığında.
tgkprog

12

Dize değişmezleri ve sabitleri varsayılan olarak stajyerdir. Yani, "foo" == "foo"(String değişmezleri tarafından bildirildi), ancak new String("foo") != new String("foo").


4
Yani, soru ne zaman kullanmalıyız intern,
Rakesh Juyal

Bu, stackoverflow.com/questions/1833581/when-to-use-intern ve bazıları dünden olmak üzere bir dizi başka soruya işaret etti .
Bozho

Bu ifade için anlayışımın String literals and constants are interned by defaultdoğru olup olmadığını bana bildirin: ,. new String("foo")-> Burada, String havuzunda bir String değişmezi "foo" oluşturulur ve diğeri yığın halinde oluşturulur, böylece toplam 2 nesne oluşturulur.
dkb

8

Java String Intern'i öğrenin - bir kez herkes için

Java dizeleri tasarım gereği değişmez nesnelerdir. Bu nedenle, aynı değerde bile iki dize nesnesi varsayılan olarak farklı nesneler olacaktır. Ancak, hafızadan tasarruf etmek istersek, aynı hafızayı string stajyer olarak adlandırılan bir kavramla kullanmayı gösterebiliriz.

Aşağıdaki kurallar kavramı açık bir şekilde anlamanıza yardımcı olacaktır:

  1. String sınıfı, başlangıçta boş olan bir stajyer havuzu tutar. Bu havuz, yalnızca benzersiz değerlere sahip dize nesneleri içerme garantisi vermelidir.
  2. Aynı değere sahip tüm dize değişmezleri aynı bellek konumu nesnesi olarak düşünülmelidir, aksi takdirde ayrım kavramı yoktur. Bu nedenle, aynı değere sahip tüm bu değişmezler, stajyer havuzuna tek bir giriş yapacak ve aynı bellek konumuna atıfta bulunacaktır.
  3. İki veya daha fazla değişmezin birleştirilmesi de değişmezdir. (Bu nedenle kural # 2 onlar için geçerli olacaktır)
  4. Nesne olarak oluşturulan her dize (örneğin, değişmez değer dışındaki herhangi bir yöntemle) farklı bellek konumlarına sahip olacak ve stajyer havuzuna herhangi bir giriş yapmayacaktır.
  5. Değişmez değerlerin değişmez değerlerle birleştirilmesi, değişmez bir değer oluşturur. Böylece, ortaya çıkan nesne yeni bir bellek konumuna sahip olacak ve stajyer havuzuna bir giriş YAPMAYACAKTIR.
  6. Bir dize nesnesinde stajyer yöntemini çağırmak, stajyer havuzuna giren yeni bir nesne oluşturur veya varolan bir nesneyi aynı değere sahip havuzdan döndürür. Stajyer havuzunda olmayan herhangi bir nesnenin çağrılması, nesneyi havuza taşımaz. Bunun yerine havuza giren başka bir nesne oluşturur.

Misal:

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

Not: Yaylı stajyer için motivasyonel durumlar burada tartışılmamaktadır. Ancak, bellek tasarrufu kesinlikle birincil hedeflerden biri olacaktır.


# 3 için teşekkür ederim, bilmiyordum :)
kaay

4

derleme zamanı ve çalışma zamanı zamanı olmak üzere iki periyot süresi yapmanız gerekir. örneğin:

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

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

bir yandan, örnek 1'de, sonuçların tümünün doğru olduğunu görüyoruz, çünkü derleme zamanında jvm, "test" ifadesini hazır dizelerin havuzuna koyacaktır, jvm bulma "testi" varsa, var olanı kullanır, örnek 1'de "test" dizeleri aynı bellek adresini gösterir, bu nedenle örnek 1 true değerini döndürür. diğer yandan, örnek 2'de, substring () yöntemi çalışma zamanında yürütülür, "test" == "! test" .substring (1) durumunda, havuz iki dize nesnesi oluşturur " test "ve"! test ", bu yüzden farklı referans nesneleridir, bu nedenle" test "=="! test ".substring (1) .intern (), stajyer yöntemi () ) değişmez dizeler havuzuna ""! test ".substring (1)"


3

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

string interning, değişmez olması gereken her farklı dize değerinin yalnızca bir kopyasını saklama yöntemidir. Staj dizeleri, dizge oluşturulduğunda veya staj yaparken daha fazla zaman gerektirme pahasına, bazı dizgi işleme görevlerini daha fazla zaman veya alan açısından verimli hale getirir. Farklı değerler bir dize stajyer havuzunda saklanır.


2

Stajyer Dizeler yinelenen Dizelerden kaçınır. Interning, yinelenen Dizeleri algılamak ve değiştirmek için RAM'i daha fazla CPU zamanından kurtarır. Kaç tane referans gösterirse gösterilsin, her bir String'in sadece bir kopyası vardır. Dizeler değiştirilemez olduğundan, iki farklı yöntem de aynı Dizeyi kullanıyorsa, aynı Dizenin bir kopyasını paylaşabilirler. Çoğaltılan Dizeleri paylaşılan dizelere dönüştürme işlemine interning.String.intern () adı verilir ve kurallı ana Dize'nin adresi verilir. Stajyer Dizeleri eşit yerine basit == (işaretçileri karşılaştıran) ile karşılaştırabilirsinizDize karakterlerini birer birer karşılaştıran. Dizeler değişmez olduğu için, stajyer işlemi, "su aygırı" gibi başka bir değişmez öğenin alt dizesi olarak var olduğunda, "pot" için ayrı bir String değişmezi oluşturmayarak alandan daha fazla tasarruf etmekte serbesttir.

Daha fazla http://mindprod.com/jgloss/interned.html görmek için


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");
        }

ÇIKTI

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");

İki dize bağımsız olarak oluşturulduğunda, intern()bunları karşılaştırmanıza olanak tanır ve ayrıca referans daha önce mevcut değilse, dize havuzunda bir başvuru oluşturmanıza yardımcı olur.

Kullandığınızda String s = new String(hi), java dizenin yeni bir örneğini oluşturur, ancak kullandığınızda String s = "hi"java kodda "hi" kelimesinin bir örneğinin olup olmadığını denetler ve varsa, başvuruyu döndürür.

Dizeleri karşılaştırmak referansa dayandığından, intern()bir referans oluşturmanıza yardımcı olur ve dizelerin içeriğini karşılaştırmanıza izin verir.

Eğer kullandığınız zaman intern()kodu, aynı nesneye atıfta dize tarafından kullanılan alanın temizler ve sadece bellekte zaten mevcut olan aynı nesnenin başvuru döndürür.

Ancak kullanırken p5 olması durumunda:

String p5 = new String(p3);

Yalnızca p3 içeriği kopyalanır ve p5 yeni oluşturulur. Yani stajyer değil .

Böylece çıktı:

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 () yöntemi, string sabit havuzunda yığın string nesnesinin tam bir kopyasını oluşturmak için kullanılır. Dize sabit havuzundaki dize nesneleri otomatik olarak stajyerdir, ancak öbekteki dize nesneleri yoktur. Staj oluşturmanın ana kullanımı, bellek alanından tasarruf etmek ve dize nesnelerinin daha hızlı karşılaştırılmasını sağlamaktır.

Kaynak: Java dize stajyer nedir?


1

Söylediğiniz gibi, bu dize intern()yöntemi önce String havuzundan bulur, bulursa, bunu gösteren nesneyi döndürür veya havuza yeni bir String ekler.

    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

s1Ve s2dize havuzu "Merhaba" olduğuna işaret eder ve kullanan iki nesnelerdir "Hello".intern()olduğunu göreceksiniz s1ve s2. Bu yüzden "s1 == s3"doğru yanı sıra döner s3.intern().


Bu pek fazla yeni bilgi sağlamaz. Zaten istisna bir cevap var.
Alexander

0

Karşılık gelen dize sabit havuz nesne başvurusunu almak istiyorsak, yığın nesne başvurusunu kullanarak , intern () için gitmeliyiz

String s1 = new String("Rakesh");
String s2 = s1.intern();
String s3 = "Rakesh";

System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true

Resimsel Görünüm resim açıklamasını buraya girin

Adım 1: 'Rakesh' verisine sahip nesne, yığın ve dize sabit havuzunda oluşturulur. Ayrıca s1 her zaman yığın nesnesini işaret eder.

Adım 2: yığın nesne başvurusu s1'i kullanarak, intern () kullanarak karşılık gelen dize sabit havuz nesnesi referenc s2'yi almaya çalışıyoruz

Adım 3: Dize sabiti havuzunda 'Rakesh' verisine sahip bir nesne oluşturma, s3 adıyla

"==" operatörü referans karşılaştırma içindir.

Alma false s1 == s2

Başlarken gerçek s2 == s3 için

Umarım bu yardım !!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.