String Java'da neden değişmez?


78

Bunun nedenini anlayamadım. Her zaman diğer geliştiriciler gibi String sınıfını kullanırım, ancak değerini değiştirdiğimde, yeni String örneği oluşturulur.

Java'da String sınıfının değişmezliğinin nedeni ne olabilir?

StringBuffer veya StringBuilder gibi bazı alternatifler olduğunu biliyorum. Bu sadece merak.


20
Teknik olarak, bu bir kopya değil, ama Eric Lippert bu soruya burada büyük bir cevap veriyor: programmers.stackexchange.com/a/190913/33843
Heinzi

Yanıtlar:


105

eşzamanlılık

Java başlangıçtan itibaren eşzamanlılık düşünceleriyle tanımlandı. Sıkça bahsedildiği gibi, paylaşılan mutable problemlidir. Bir şey, başka bir ipliğin arkasında, o ipliğin farkında olmadan başka birini değiştirebilir.

Paylaşılan bir dize nedeniyle ortaya çıkan bir çok iş parçacıklı C ++ hata var - bir modül koddaki başka bir modül ona bir işaretçi kaydettiğinde ve aynı kalmasını beklediğinde değiştirmenin güvenli olduğunu düşünüyordu.

Bunun çözümü, her sınıfın kendisine aktarılan değiştirilebilir nesnelerin savunma kopyasını çıkarmasıdır. Değişken dizgelerde, kopya yapmak için bu O (n) 'dir. Değişmez dizeler için kopya yapmak O (1) 'dir, çünkü kopya değildir, değiştirilemeyen aynı nesnedir.

Çok iş parçacıklı bir ortamda, değişmez nesneler her zaman birbirleriyle güvenli bir şekilde paylaşılabilir. Bu, bellek kullanımında genel bir azalmaya yol açar ve bellek önbelleğe almayı iyileştirir.

Güvenlik

Dizeler çoğu zaman yapıcıların argümanları olarak iletilir - ağ bağlantıları ve protokoller en kolay akla gelen ikisidir. Bunu daha sonra uygulamada belirsiz bir zamanda değiştirebilmek, güvenlik sorunlarına yol açabilir (işlev, bir makineye bağlandığını düşündü, ancak diğerine yönlendirildi, ancak nesnedeki her şey, ilkine bağlı gibi görünüyor ... onun bile aynı dize).

Java tek bir yansıma kullanmasına izin verir - ve bunun için parametreler dizgedir. Birinden bir dize geçirme tehlikesi yansıtan başka bir yönteme giderken modifiye edilebilir. Bu çok kötü.

Hash Anahtarları

Karma tablo, en çok kullanılan veri yapılarından biridir. Veri yapısının anahtarları çok sık dizelidir. Değişmez dizgelere sahip olmak (yukarıdaki gibi) karma tablosunun her seferinde karma anahtarının bir kopyasını yapmasına gerek olmadığı anlamına gelir. Eğer dizeler değişebilirse ve karma tablo bunu yapmadıysa, karma anahtarını bir mesafeden değiştirmek bir şey mümkün olacaktır.

Java'daki Nesnenin çalışma şekli, her şeyin bir hash anahtarına sahip olmasıdır (hashCode () yöntemiyle erişilir). Değişmez bir dizgeye sahip olmak, hashCode öğesinin önbelleğe alınabileceği anlamına gelir. Dizelerin bir karma değerin anahtarları olarak ne sıklıkta kullanıldığı göz önüne alındığında, bu önemli bir performans artışı sağlar (karma kodunu her seferinde yeniden hesaplamak yerine).

altdizgelerin

String değişmez olması sayesinde, veri yapısını destekleyen temel karakter dizisi de değişmezdir. Bu, yapılması gereken substringyöntem üzerinde belirli optimizasyonlara izin verir ( mutlaka yapılması gerekmez - ayrıca bazı bellek sızıntıları olasılığını da beraberinde getirir).

Yaparsan:

String foo = "smiles";
String bar = foo.substring(1,5);

Değeri bar'mil'. Bununla birlikte, her ikisi de foove baraynı karakter dizisi tarafından desteklenebilir, daha fazla karakter dizisinin başlatılmasını azaltır veya kopyalar - yalnızca dizgideki farklı başlangıç ​​ve bitiş noktalarını kullanır.

foo | | (0, 6)
    vv
    gülümsüyor
     ^ ^
bar | | (1, 5)

Şimdi, bunun dezavantajı (bellek sızıntısı) eğer biri 1k uzunluğunda bir dizeye sahipse ve birinci ve ikinci karakterin alt dizisini aldıysa, aynı zamanda 1k uzun karakter dizisi tarafından da desteklenecektir. Bu dizi, tüm karakter dizisinin değerine sahip orijinal dize çöp toplanmış olsa bile bellekte kalır.

Bunu JDK 6b14'den String'de görebilirsiniz (aşağıdaki kod GPL v2 kaynağındandır ve örnek olarak kullanılır).

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

Alt dizinin, dizinin herhangi bir kopyasını içermeyen ve çok daha hızlı olacak olan paket düzeyindeki String yapıcısını nasıl kullandığına dikkat edin (büyük dizileri çoğaltmasa da, bazı büyük dizilerin etrafını tutmanın pahasına).

Yukarıdaki kodun Java 1.6 için olduğunu unutmayın. Belgelenen olarak alt dize yapıcısı uygulandığı yolu Java 1.7 ile değiştirildi Java 1.7.0_06 yapılan dize dahili gösterime Değişiklikler - Sorunum yukarıda bahsedilen bu bellek sızıntısı bing. Java muhtemelen çok fazla String işlemi olan bir dil olarak görülmedi ve bu nedenle bir alt dize için performans artışı iyi bir şeydi. Şimdi, asla toplanmayan dizgilerde depolanan büyük XML belgeleriyle, bu bir sorun haline gelir ... ve böylece Stringalt dizideki aynı diziyi kullanmama değişikliği , böylece daha büyük karakter dizisi daha hızlı toplanabilir.

Yığını kötüye kullanma

Bir verebilir dize değerini etrafında yerine değişkenlikle sorunları önlemek için değişmez dize başvurusunu geçirin. Ancak, büyük dizelerle, bunu istif üzerinde geçirmek ... sisteme zarar verir (tüm xml belgelerini istif olarak dizeler olarak koymak ve sonra bunları çıkarmak veya devam ettirmek ...).

Tekilleştirme olasılığı

Kabul edilirse, bu, Dizelerin neden değişmez olması gerektiği için bir ilk motivasyon değildi, ancak biri değişmez Dizelerin neden iyi bir şey olduğu rasyoneline bakıldığında, bu kesinlikle dikkate alınması gereken bir şey.

Dizelerle biraz çalışan herkes hafızayı emebileceklerini biliyor. Bu, özellikle bir süre etrafta dolaşan veritabanlarından veri çekmek gibi şeyler yaparken geçerlidir. Bu sokmalar ile defalarca, tekrar tekrar aynı dizge olurlar (her satır için bir kez).

Birçok büyük ölçekli Java uygulaması şu anda belleğe tıkanmıştır. Ölçümler, bu tür uygulamalarda ayarlanan Java yığını canlı verilerinin yaklaşık% 25'inin String nesneleri tarafından tüketildiğini göstermiştir. Ayrıca, bu String nesnelerinin kabaca yarısı kopyalardır, kopyaların string1.equals (string2) true olduğu anlamına gelir. Öbek üzerinde yinelenen String nesnelerine sahip olmak, esasen, sadece bir hafıza kaybıdır. ...

Java 8 güncellemesi 20 ile JEP 192 (yukarıda belirtilen motivasyon) bu sorunu çözmek için uygulanmaktadır. String tekilleştirme işleminin nasıl çalıştığının ayrıntılarına girmeden, String'lerin kendilerinin değişmez olması esastır. StringBuilders'ı tekilleştiremezsiniz çünkü değişebilirler ve birisinin altınızdan bir şey değiştirmesini istemezsiniz. Değiştirilemez Dizeler (bu String havuzuyla ilişkili), içinden geçebileceğiniz ve aynı olan iki dizeyi bulduğunuzda, bir dize başvurusunu diğerine işaret edebilir ve çöp toplayıcısının yeni kullanılmamış olanı kullanmasına izin verebilirsiniz.

Diğer diller

Nesnel C (Java'dan önce gelen) NSStringve NSMutableString.

C # ve .NET, varsayılan dizenin değişmez olduğu için aynı tasarım seçimlerini yaptı.

Lua dizeleri de değişmezdir.

Python da.

Tarihsel olarak, Lisp, Scheme, Smalltalk hepsi dizgede stajyerdir ve bu nedenle değişmez olmalarını sağlar. Daha modern dinamik diller genellikle dizeleri değişmez olmalarını gerektiren bir şekilde kullanır (bir String olmayabilir , ancak değişmezdir).

Sonuç

Bu tasarım düşünceleri birçok dilde tekrar tekrar yapılmıştır. Değişmez dizgelerin bütün garip olmaları için alternatiflerden daha iyi olduğu ve genel olarak daha iyi kod (daha az hata) ve daha hızlı çalıştırılabilir sonuçlara yol açtığı genel görüş birliğidir.


3
Java değişken ve değişmez dizeler sağlar. Bu cevap, değişken dizelerde yapılabilecek bazı performans avantajlarını ve değişmeyen verileri seçmesinin bazı nedenlerini açıklar; ancak değişmez sürümün neden varsayılan sürüm olduğunu tartışmıyor.
Billy ONeal

3
@BillyONeal: güvenli bir varsayılan ve güvenli olmayan bir alternatif, neredeyse her zaman zıt yaklaşımdan daha güvenli sistemlere neden olur.
Joachim Sauer

4
@BillyONeal Değişmezler varsayılan değilse, eşzamanlılık, güvenlik ve sağlama sorunları daha yaygın olacaktır. Dil tasarımcıları, programcı verimliliğini arttırmaya çalışacak bir dizi ortak hatanın önlenmesine çalışmak için varsayılanların ayarlandığı bir dil yapmak için (kısmen C'ye cevap olarak) seçtiler (artık bu hatalar için endişelenmenize gerek yok). Değiştirilebilen dizelerle değiştirilebilen dizelere göre daha az hata (açık ve gizli) vardır.

@Joachim: Başka türlü hak iddia etmiyorum.
Billy ONeal

1
Teknik olarak, Common Lisp "string benzeri" işlemler için değişken dizgelere ve değişken tanımlayıcılar için değişmez isimleri olan sembollere sahiptir.
Vatine,

21

Hatırlayabildiğim sebepler:

  1. Dize havuzu değiştirilemez hale getirilmeden dize havuzu özelliği hiç mümkün değildir, çünkü dizge havuzunda bir dizge nesnesi / değişmez örn. "XYZ" birçok referans değişken tarafından referans alınacaktır, bu nedenle bunlardan herhangi biri değişirse, diğerleri otomatik olarak etkilenecektir. .

  2. Dize, örneğin, ağ bağlantısını açmak, veritabanı bağlantısını açmak, dosyaları açmak için birçok java sınıfı için parametre olarak yaygın şekilde kullanılmıştır. String değişmez değilse, bu ciddi bir güvenlik tehdidine yol açacaktır.

  3. Immutability, String'in karma kodunu önbelleğe almasına izin verir.

  4. İplik güvenli hale getirir.


7

1) Dize Havuzu

Java tasarımcısı, String'in tüm Java uygulamalarında en çok kullanılan veri türü olacağını bilmektedir ve bu yüzden en baştan optimize etmek istediler. Bu yöndeki en önemli adımlardan biri, String değişmezlerini String havuzunda saklama fikriydi. Amaç, geçici String nesnesini paylaşarak azaltmaktı ve paylaşmak için Immutable sınıfından olmak zorundalar. Değişken bir nesneyi, birbirleriyle bilinmeyen iki tarafla paylaşamazsınız. İki referans değişkeninin aynı String nesnesine işaret ettiği varsayımsal bir örneği ele alalım:

String s1 = "Java";
String s2 = "Java";

Şimdi eğer s1 nesneyi "Java" 'dan "C ++"' ya değiştirirse, başvuru değişkeni s2 = "C ++" değerini aldı, ki bu bile bilmiyor. String değişmez yapılarak, String değişmezinin bu paylaşımı mümkün oldu. Kısacası, String havuzunun temel fikri, String'i finalde veya Java'ya sığmaz hale getirmeden uygulanamaz.

2) Güvenlik

Java, her hizmet düzeyinde güvenli bir ortam sağlama konusunda açık bir hedefe sahiptir ve String, tüm bu güvenlik işlerinde kritik öneme sahiptir. String birçok Java sınıfı için yaygın olarak kullanılmaktadır, örneğin ağ bağlantısını açmak için host ve portu String olarak geçirebilirsiniz, Java'daki dosyaları okumak için dosya yolunu ve dizini String olarak ve veritabanı bağlantısını açmak için kullanabilirsiniz Dize olarak veritabanı URL'sini iletin. String değişmez değilse, bir kullanıcı sistemdeki belirli bir dosyaya erişim izni vermiş olabilir, ancak kimlik doğrulama işleminden sonra PATH'i başka bir şeyle değiştirebilir, bu ciddi güvenlik sorunlarına neden olabilir. Benzer şekilde, veritabanına veya ağdaki başka herhangi bir makineye bağlanırken, String değerini değiştirmek güvenlik tehditleri oluşturabilir. Değişken dizeler de Yansımada güvenlik sorununa neden olabilir,

3) Sınıf Yükleme Mekanizmasında Dize Kullanımı

String'i final veya Immutable yapmak için başka bir neden, sınıf yükleme mekanizmasında yoğun olarak kullanılmasından kaynaklanıyordu. String tartıştığı için bir saldırgan bu durumdan yararlanabilir ve örneğin java.io.Reader gibi standart Java sınıflarını yükleme isteği, kötü amaçlı sınıf com.unknown.DataStolenReader olarak değiştirilebilir. String'i final ve değişmez tutarak, en azından JVM'nin doğru sınıfları yüklediğinden emin olabiliriz.

4) okuyucunun faydaları

Concurrency ve Multi-threading, Java'nın anahtar önerisi olduğundan, String nesnelerinin iş güvenliği hakkında düşünmek çok mantıklı geldi. String'in yaygın olarak kullanılması beklendiğinden, Immutable'ın harici senkronizasyon olmadığı anlamına gelir, String'in birden fazla thread arasında paylaşılmasını içeren daha temiz kod demektir. Bu tek özellik, zaten karmaşık, kafa karıştırıcı ve hataya açık eşzamanlılık kodlamasını çok daha kolay hale getirir. String değişmez olduğundan ve sadece threadlar arasında paylaştığımız için daha okunabilir kodlar verir.

5) Optimizasyon ve Performans

Şimdi bir sınıfı imkansız hale getirdiğinizde, önceden bildiğiniz gibi, bu sınıf bir kez yaratıldığında değişmeyecek. Bu, önbellekleme gibi birçok performans optimizasyonu için açık bir yol sağlar. Dize kendisi biliyor, ben değişmeyeceğim, bu yüzden Dize hashcode önbelleğe alır. Hatta hashcode'u tembel olarak hesaplar ve oluşturulduktan sonra önbelleğe alır. Basit dünyada, herhangi bir String nesnesinin hashCode () yöntemini ilk çağırdığınızda, hash kodunu hesaplar ve sonraki tüm hashCode () çağrısını önceden hesaplanmış, önbelleğe alınmış değer döndürür. Bu, iyi bir performans kazancı sağlar, çünkü String, Hash tabanlı ve HashMap gibi karma tabanlı Haritalar'da yoğun olarak kullanılır. Hash kodunun önbelleğe alınması, String'in içeriğine bağlı olduğundan, sabit ve nihai hale getirilmeden mümkün değildi.


5

Java Sanal Makinesi, başka türlü gerçekleştirilemeyen dizgi işlemleriyle ilgili birkaç optimizasyon gerçekleştirir. Örneğin, "Mississippi" değerine sahip bir dizeniz varsa ve "Mississippi" .substring (0, 4) 'ü başka bir dizgiye atadıysanız, bildiğiniz kadarıyla "Miss" yapmak için ilk dört karakterin bir kopyası yapıldı. . Bilmediğiniz şey, hem sahibi hem de diğeri için 0'dan 4'e bir referansı olan aynı orijinal "Mississippi" dizisini paylaşmasıdır. (Sahibine yapılan başvuru sahibinin tarafından alınmasını engeller. sahibi kapsam dışına çıktığında çöp toplayıcı)

Bu "Mississippi" kadar küçük bir dize için önemsizdir, ancak daha büyük dizeler ve çoklu işlemlerle dizeyi kopyalamak zorunda kalmamak büyük bir zaman tasarrufu sağlar! Eğer dizeler değişebilirse, o zaman bunu yapamazsınız, çünkü orijinali değiştirmek alt dizeyi "kopyaları" da etkileyecektir.

Ayrıca, Donal'ın belirttiği gibi, avantaj, dezavantajı nedeniyle büyük ölçüde azaltılacaktır. Bir kütüphaneye bağlı bir program yazdığınızı ve bir dize döndüren bir işlev kullandığınızı hayal edin. Bu değerin sabit kalacağından nasıl emin olabilirsiniz? Böyle bir şeyin olmamasını sağlamak için her zaman bir kopya çıkarmanız gerekir.

Aynı dizeyi paylaşan iki konu varsa? Şu anda başka bir konu tarafından yeniden yazılmış bir dize okumak istemezsiniz, değil mi? Bu nedenle, dize, ortak sınıf olan, neredeyse her Java programını bu kadar yavaşlatır. Aksi takdirde, o dizgeyi gerektiren her iş parçacığı için bir kopya yapmanız gerekecek ya da o dizeyi kullanarak kodu her ikisi de yalnızca programınızı yavaşlatan bir eşitleme bloğuna yerleştirmeniz gerekecektir.

Bütün bu nedenlerden ötürü, Java'yı C ++ 'dan ayırt etmek için verilen ilk kararlardan biriydi.


Teorik olarak, eğer paylaşılıyorsa mutasyona kopyalanmasına izin veren çok katmanlı bir tampon yönetimi yapabilirsiniz, ancak çok iş parçacıklı bir ortamda verimli bir şekilde çalışmak çok zordur.
Donal Fellows

@DonalFellows Java Sanal Makinesi Java ile yazılmış olmadığı için (açıkça), paylaşılan işaretçiler veya benzeri bir şey kullanarak dahili olarak yönetildiğini varsaydım.
Neil

5

Dizenin değişmezliğinin nedeni, dilin diğer ilkel türleriyle tutarlılıktan kaynaklanmaktadır. Bir varsa intdeğerini 42 içeren ve buna değer 1 eklersek 42. Sen başlangıç değerlerine tamamen ilgisiz yeni bir değer, 43, olsun değişmez. İpten başka ilkellerin mutasyona uğraması kavramsal bir anlam ifade etmemektedir; ve karakter dizilerini değişmez olarak ele alan programlar hakkında düşünmesi ve anlaşılması genellikle daha kolaydır.

Üstelik Java, gördüğünüz gibi gerçekten hem değişken hem de değişken dizeler sağlar StringBuilder; gerçekten, yalnızca varsayılan , değiştirilemez dizedir. Referansları StringBuilderher yere iletmek istiyorsanız, bunu kesinlikle yapabilirsiniz. Java, bu kavramlar için ayrı tipler ( Stringve StringBuilder) kullanır, çünkü kendi tip sistemindeki değişkenliği veya eksikliğini ifade etme desteği yoktur. Tip sistemlerindeki değişmezliği destekleyen dillerde (örneğin, C ++ 'lar const), genellikle her iki amaca hizmet eden tek bir dize türü vardır.

Evet, dizginin değişmez olması, interning gibi değişmez dizgelere özgü bazı optimizasyonların uygulanmasına izin verir ve dize referanslarının dişler arasında senkronizasyon olmadan geçirilmesine izin verir. Ancak, bu mekanizmayı bir dilin amaçlanan amacı ile basit ve tutarlı bir tür sistemle karıştırır. Bunu herkesin çöp toplama yolunda nasıl yanlış düşündüğünü düşündüğünü düşünüyorum; çöp toplama "kullanılmayan hafızanın geri kazanılması" değildir; "sınırsız belleğe sahip bir bilgisayarı simüle ediyor" . Tartışılan performans optimizasyonları, değişken karakter dizilerinin amacının gerçek makinelerde iyi performans göstermesini sağlamak için yapılan şeylerdir; ilk başta bu tür dizgelerin değişmez olmasının nedeni değil.


@ Billy-Oneal .. ile ilgili olarak "42 değerini içeren bir int varsa ve buna 1 değerini eklerseniz, 42 değerini değiştirmezsiniz. Yeni bir değer elde edersiniz. değerler." Bundan emin misin?
Shamit Verma

@Shamit: Evet, eminim. 43'e 1 ila 42 sonuç
eklenir.

@Shamit: Benzer şekilde, gibi bir şey yapamazsınız 43 = 6ve 43 sayısının 6 sayıyla aynı anlama
gelmesini beklemezsiniz

int i = 42; i = i + 1; bu kod 42'yi hafızaya
kaydeder

@Shamit: Bu durumda, mutasyona uğradın i42 değil string s = "Hello "; s += "World";. Düşünün . Değişkenin değerini değiştirdiniz s. Ama dizeleri "Hello ", "World"ve "Hello World"değişken değildir.
Billy ONeal

4

İmkansızlık, sahip olmadığınız sınıfların tuttuğu değişimlerin değiştirilemeyeceği anlamına gelir. Değiştirilmemelidir Java uygulamasının çekirdeğinde olanları kapsar ait olmayan Sınıflar ve dizeleri Sen vb güvenlik belirteçleri, hizmet adresleri gibi şeyleri içerir gerçekten bu tür modifiye edebilmesi olmamalı şeylerin (ve korumalı alan modunda çalışırken bu iki kat uygulanır).

String değişmez ise, dizinin içeriğinin ayağının altında değişmesini istemeyen bir bağlamdan her aldığınızda, “tam olarak” bir kopyasını almanız gerekir. Bu çok pahalı olur.


4
Bu tam olarak aynı argüman sadece herhangi bir türe uygulanmaz String. Ancak, örneğin, Arrayyine de değişkendir. Öyleyse neden Stringdeğişmez ve Arraydeğil. Değişmezlik çok önemliyse, Java neden değişmez nesneler yaratmayı ve çalışmayı bu kadar zorlaştırıyor?
Jörg W Mittag

1
@ JörgWMittag: Bunun temel olarak ne kadar radikal olmak istedikleri sorusu olduğunu farz ediyorum. Değişmez bir String'e sahip olmak, Java 1.0 günde oldukça radikaldi. Aynı zamanda (öncelikle veya hatta yalnızca) değişmez bir koleksiyon çerçevesine sahip olmak, dili geniş ölçüde kullanamayacak kadar radikal olabilir.
Joachim Sauer

Etkili bir değişmez koleksiyon çerçevesi yapmak, performans sergilemek için oldukça zordur, böyle bir şey yazmış (Java dilinde değil) biri olarak konuşmak. Ayrıca, tamamen dizilerimde olmasını diliyorum; Bu beni biraz işten kurtardı.
Donal Fellows,

@DonalFellows: pcollections sadece bunu yapmayı hedefliyor (bununla birlikte asla kullanmadım).
Joachim Sauer

3
@ JörgWMittag: iddia ediyorum (genellikle tamamen işlevsel açıdan) insan vardır her türlü değişmez olmalıdır. Benzer şekilde, paralel ve eşzamanlı yazılımda değişken durumlarla çalışmakla ilgilenen tüm konuları bir araya getirirseniz, değişken nesnelerle çalışmanın genellikle değişkenlerden çok daha kolay olduğunu kabul edersiniz .
Steven Evers,

2

Bazı verileri kabul ettiğiniz bir sistem hayal edin, doğruluğunu onaylayın ve sonra iletin (örneğin bir DB'de depolanacak).

Verilerin a olduğu Stringve en az 5 karakter uzunluğunda olması gerektiği varsayılmaktadır . Yöntemin şuna benziyor:

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

Şimdi, storeInDatabaseburada çağrıldığında inputgereksinimin uygun olacağını kabul edebiliriz . AncakString değişken olsaydı , arayan kişi doğrulandıktan hemen sonra ve veritabanında depolanmadan önceinput nesneyi (başka bir diziden) değiştirebilirdi . Bu, iyi zamanlama gerektirir ve muhtemelen her seferinde iyi gitmezdi, ancak bazen veritabanında geçersiz değerler depolamanızı sağlayabilir.

Değiştirilemeyen veri türleri bu (ve bununla ilişkili) sorunlara çok basit bir çözümdür: bir değeri ne zaman kontrol ederseniz , kontrol edilen koşulun daha sonra hala geçerli olduğuna bağlı olabilirsiniz .


Açıklama için teşekkürler. Peki ya böyle bir tanıtıcı yöntemini çağırırsam; tanıtıcı (yeni String (giriş + "naberlan")). Galiba db'de geçersiz değerler var.
yfklon

1
@blank: beri de, inputbir handleyöntemi (ne olursa olsun zaten çok uzun orijinal input olduğu), sadece bir istisna olacaktır. Yöntemi çağırmadan önce yeni bir giriş oluşturuyorsunuz . Problem değil.
Joachim Sauer

0

Genel olarak, değer türleri ve referans türleri ile karşılaşırsınız . Bir değer türüyle, onu temsil eden nesneyi önemsemezsiniz, değeri önemsersiniz. Sana bir değer verirsem, bu değerin aynı kalmasını beklersin. Birdenbire değişmesini istemezsin. 5 sayısı bir değerdir. Aniden 6'ya değişmesini beklemiyorsun. "Merhaba" dizesi bir değerdir. Birdenbire "P *** kapalı" olarak değişmesini beklemiyorsunuz.

İle başvuru türleri nesnenin önemsemez, ve bunu değiştirmek için bekliyoruz. Örneğin, genellikle bir dizinin değişmesini beklersiniz. Sana bir dizi verirsem ve aynen olduğu gibi tutmak istiyorsan, değiştirmemem için bana güvenmelisin, ya da bir kopyasını çıkar.

Java string sınıfıyla, tasarımcıların bir karar vermesi gerekiyordu: Dizelerin bir değer türü gibi davranması daha mı iyi, yoksa bir referans türü gibi davranması mı daha iyi? Java dizgileri söz konusu olduğunda, değer türleri olmaları gerektiğine, yani nesneler oldukları için değişmez nesneler olmalarına karar verildi.

Aksi bir karar verilebilirdi, fakat bence çok fazla baş ağrısına yol açacaktı. Başka yerlerde de belirtildiği gibi, birçok dil aynı kararı verdi ve aynı sonuca vardı. Bunun bir istisnası, bir dize sınıfına sahip olan C ++ ve dizeleri sabit veya sabit olabilir, ancak C ++ 'da, Java'nın aksine, nesne parametreleri referans olarak değil, değerler olarak iletilebilir.


0

Hiç kimsenin bunu işaret etmediğine şaşırdım.

Cevap: Değişken olsa bile, size önemli bir fayda sağlamaz. Ek sorun çıkardığı sürece size faydası olmaz. En yaygın iki mutasyon vakasını inceleyelim:

Bir dizenin bir karakterini değiştirme

Bir Java dizesindeki her karakter 2 veya 4 bayt aldığından, kendinize sorun, mevcut kopyayı değiştirebilseydiniz bir şey kazanır mıydınız?

Senaryoda 2 baytlık bir karakteri 4 baytlık bir karakterle (veya tersi) değiştiriyorsanız, dizenin kalan kısmını 2 bayt sola veya sağa kaydırmanız gerekir. Bu, tüm dizgiyi hesaplamalı bakış açısıyla tamamen kopyalamaktan farklı değildir.

Bu aynı zamanda genellikle istenmeyen bir durumdur. Uygulamayı İngilizce metinle test eden birisini hayal edin ve başvuru Çin gibi yabancı ülkelere kabul edildiğinde, her şey garip bir şekilde performans göstermeye başlar.

Var olana başka bir dize (veya karakter) ekleme

İki rastgele dizeniz varsa, bunlar iki ayrı hafıza konumunda otururlar. İlkini ikincisini ekleyerek değiştirmek istiyorsanız, ilk dizenin sonunda ek bellek isteyemezsiniz, çünkü muhtemelen zaten dolu.

Birleştirilen dizeyi tamamen yeni bir konuma kopyalamanız gerekir; bu, her iki dizenin de değişmez olmasıyla aynıdır.

Etkili bir şekilde ekleme yapmak istiyorsanız StringBuilder, bir dizgenin sonunda oldukça muhtemel bir alan eklemek için kullanmak isteyebilirsiniz .


-2
  1. pahalıdırlar ve değişken olmalarını sağlamak ana dizginin bayt dizisini paylaşan alt dizeler gibi şeylere izin verir. (yeni bayt dizisi oluşturup kopyalamanız gerekmediğinden hız artışı)

  2. güvenlik - paketinizin veya sınıf kodunuzun yeniden adlandırılmasını istemezsiniz

    [eski 3 kaldırıldı, StringBuilder src'ye baktı - dizeyi hafızayla paylaşmadı (değiştirilinceye kadar) Sanırım 1.3 ya da 1.4 idi]

  3. önbellek hashcode

  4. Mutalble dizeleri için SB kullanın (gerektiğinde oluşturucu veya tampon)


2
1. Elbette, eğer bu gerçekleşirse ipin daha büyük kısımlarını tahrip etmemenin cezası vardır. Interning ücretsiz değil; Gerçi birçok gerçek dünya programında performansı arttırıyor. 2. Bu gereksinimi karşılayabilecek kolaylıkla "string" ve "ImmutableString" olabilir. 3. Bunu anladığımdan emin değilim ...
Billy ONeal 17:13

0,3. karma kodunu önbelleğe almalıydı. Bu da değiştirilebilir bir dizeyle yapılabilir. @ billy-oneal
tgkprog

-4

Dizeler Java'da ilkel bir veri türü olmalıydı. Olsaydı, o zaman dizeler varsayılan olarak değişken olabilirdi ve son anahtar kelime değişmez dizeler üretecekti. Değişken dize kullanışlıdır ve bu nedenle dize, stringbuilder ve charsequence sınıflarındaki değişken dizeler için çoklu kesitler vardır.


3
Bu, şimdi sorunun sorduğu şeyin "neden" yönüne cevap vermiyor. Ayrıca, java finali bu şekilde çalışmaz. Değişken dizgiler kesmek değil, dizgilerin en yaygın kullanımlarına ve jvm'nin iyileştirilmesi için yapılabilecek optimizasyonlara dayanan gerçek tasarım konularıdır.

1
“Neden” cevabı, zayıf bir dil tasarım kararıdır. Değişken dizeleri desteklemenin üç farklı yolu, derleyicinin / JVM'nin taşıması gereken bir kesmektir.
CWallach

3
String ve StringBuffer orijinaldi. StringBuilder daha sonra StringBuffer ile bir tasarım zorluğunu tanımak için eklendi. Farklı nesneler olan değişken ve değişken dizeler, birçok dilde, tasarım tekrar tekrar yapıldığından ve her birinin her biri farklı nesne olduğuna karar verdiğinden bulunur. C # "Dizeler değişmez" ve neden .NET String değişmez? , nesnel C NSString değişmezken NSMutableString değişkendir. stackoverflow.com/questions/9544182
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.