MERHABA! Bugün çok önemli ve ilginç bir konudan bahsedeceğiz, yani nesneleri nesnelerle karşılaştırmak (Compare Strings and Equals). Öyleyse Java'da, A nesnesi tam olarak ne zaman B nesnesine eşit olur ? Bir örnek yazmaya çalışalım:
== operatörü , iki nesneyi karşılaştırmak için kullandığımızda yaklaşık olarak aynı mantığı kullanır. Peki ya programınızın farklı bir mantık kullanması gerekiyorsa? Örneğin, programınızın DNA analizi yaptığını varsayalım. İki kişinin genetik kodlarını karşılaştırır ve ikiz olup olmadıklarını belirler.
Dizi havuzu, programınızda oluşturduğunuz tüm dizi değerlerinin saklandığı bir alandır. Neden yaratıldı? Daha önce de söylediğimiz gibi, dizeler tüm nesnelerin büyük bir yüzdesini temsil eder. Herhangi bir büyük program çok sayıda dizi oluşturur. Dize havuzu bellekten tasarruf etmek için oluşturulmuştur: dizeler oraya yerleştirilir ve daha sonra oluşturulan dizeler aynı bellek alanına atıfta bulunur; her seferinde ek bellek ayırmaya gerek yoktur. String = "........" yazdığınız her seferde program, string havuzunda aynı string olup olmadığını kontrol eder. Varsa, yeni bir dizi oluşturulmaz. Ve yeni referans, dizi havuzundaki (aynı dizenin bulunduğu) aynı adresi gösterecektir. Yani biz yazdığımızda
Ve new kullanarak her yeni nesne oluşturduğunuzda , yeni dizenin içindeki metin aynı olsa bile yeni bir bellek alanı ayrılır! Görünüşe göre == operatörünü bulduk . Peki ya yeni tanıştığımız equals() yöntemi?
public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car car1 = new Car();
car1.model = "Ferrari";
car1.maxSpeed = 300;
Car car2 = new Car();
car2.model = "Ferrari";
car2.maxSpeed = 300;
System.out.println(car1 == car2);
}
}
Konsol çıktısı: false Bekle, dur. Neden bu iki araba eşit değil? Onlara aynı özellikleri atadık, ancak karşılaştırmanın sonucu yanlış. Cevap basit. == operatörü , nesne özelliklerini değil, nesne referanslarını karşılaştırır. İki nesne aynı değerlere sahip 500 alana bile sahip olabilir, ancak bunların karşılaştırılması yine de yanlış olur. Sonuçta, car1 ve car2 referanslarıiki farklı nesneye, yani iki farklı adrese işaret edin. İnsanları karşılaştırdığınız bir durumu hayal edin. Elbette, dünyanın herhangi bir yerinde sizinle aynı adı, göz rengini, yaşını, boyunu, saç rengini vb. aynı kişi.
public class Man {
int geneticCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.geneticCode = 1111222233;
Man man2 = new Man();
man2.geneticCode = 1111222233;
System.out.println(man1 == man2);
}
}
Konsol çıktısı: false Aynı mantıksal sonucu elde ederiz (çünkü çok fazla değişiklik yapmadık), ama artık bu mantık iyi değil! Ne de olsa, gerçek hayatta, DNA analizi önümüzde duran ikizlerimiz olduğuna dair bize %100 garanti vermelidir. Ancak programımız ve == operatörü bize bunun tersini söylüyor. Bu davranışı nasıl değiştiririz ve DNA eşleştiğinde programın doğru sonucu verdiğinden nasıl emin oluruz? Java'nın bunun için özel bir yöntemi vardır: equals() . Daha önce tartıştığımız toString() yöntemi gibi , equals() da diğer tüm sınıfların türetildiği Java'daki en önemli sınıf olan Object sınıfına aittir . Ama eşittir()programımızın davranışını tek başına değiştirmez:
public class Man {
String geneticCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.geneticCode = "111122223333";
Man man2 = new Man();
man2.geneticCode = "111122223333";
System.out.println(man1.equals(man2));
}
}
Konsol çıktısı: false Tam olarak aynı sonuç, peki bu yönteme ne için ihtiyacımız var? :/ Çok basit. Buradaki sorun, şu anda bu yöntemi Object sınıfında uygulandığı şekliyle kullanıyor olmamızdır. Ve eğer Object sınıfının koduna girer ve yöntemin uygulamasına bakarsak, göreceğimiz şudur:
public boolean equals(Object obj) {
return (this == obj);
}
Programın davranışının değişmemesinin nedeni budur! Aynı == işleci (referansları karşılaştıran), Object sınıfının equals() yönteminde kullanılır . Ancak bu yöntemin püf noktası, onu geçersiz kılabilmemizdir. Geçersiz kılmak, Man sınıfımızda kendi equals() yönteminizi yazmak ve ona ihtiyacımız olan davranışı vermek demektir! Şu anda, man1.equals(man2) öğesinin temelde man1 == man2 ile eşdeğer olduğu gerçeğinden hoşlanmıyoruz . İşte bu durumda yapacaklarımız:
public class Man {
int dnaCode;
public boolean equals(Man man) {
return this.dnaCode == man.dnaCode;
}
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1.equals(man2));
}
}
Konsol çıktısı: true Şimdi tamamen farklı bir sonuç elde ediyoruz! Kendi equals() yöntemimizi yazıp standart yöntem yerine onu kullanarak doğru davranışı ürettik: Şimdi iki kişi aynı DNA'ya sahipse, program "DNA analizi ikiz olduklarını kanıtladı" şeklinde rapor veriyor ve doğru çıkıyor! Sınıflarınızda equals() yöntemini geçersiz kılarak , ihtiyacınız olan nesne karşılaştırma mantığını kolayca oluşturabilirsiniz. Aslında, sadece nesne karşılaştırmasına değindik. Önümüzde, bu konuda hala büyük bir bağımsız ders var (eğer ilgileniyorsanız, şimdi gözden geçirebilirsiniz).
Java'da dizeleri karşılaştırma
Dizi karşılaştırmalarını neden diğer her şeyden ayrı değerlendiriyoruz? Gerçek şu ki, diziler programlamada başlı başına bir konudur. İlk olarak, şimdiye kadar yazılmış tüm Java programlarını alırsanız, içlerindeki nesnelerin yaklaşık %25'inin dize olduğunu görürsünüz. Dolayısıyla bu konu çok önemlidir. İkincisi, dizileri karşılaştırma süreci diğer nesnelerden gerçekten çok farklıdır. Basit bir örnek düşünün:
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = new String("CodeGym is the best website for learning Java!");
System.out.println(s1 == s2);
}
}
Konsol çıktısı: false Peki neden false aldık? Sonuçta, diziler kelimesi kelimesine tamamen aynıdır :/ Sebebini tahmin etmiş olabilirsiniz: bunun nedeni == operatörünün referansları karşılaştırmasıdır ! Açıkçası, s1 ve s2 bellekte farklı adreslere sahiptir. Bunu düşündüyseniz, örneğimizi yeniden çalışalım:
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
System.out.println(s1 == s2);
}
}
Şimdi yine iki referansımız var ama sonuç tam tersi: Konsol çıktısı: true Çaresizce kafanız mı karıştı? Neler olduğunu anlayalım. == operatörü gerçekten hafıza adreslerini karşılaştırır. Bu her zaman doğrudur ve bundan şüphe duymanıza gerek yoktur. Bu, s1 == s2 true döndürürse, bu iki dizenin aynı adrese sahip olduğu anlamına gelir . Ve gerçekten de bu doğru! Sizi dizeleri depolamak için özel bir bellek alanıyla tanıştırmanın zamanı geldi: dize havuzu
String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2, s1 ile aynı yeri işaret eder . İlk ifade, dize havuzunda yeni bir dize oluşturur. İkinci ifade, s1 ile aynı bellek alanını ifade eder . 500 özdeş dizi daha oluşturabilirsiniz ve sonuç değişmez. Bir dakika bekle. Bu doğruysa, bu örnek neden daha önce işe yaramadı?
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = new String("CodeGym is the best website for learning Java!");
System.out.println(s1 == s2);
}
}
Sanırım sezginiz size sebebini zaten söyledi =) Devamını okumadan önce tahmin etmeye çalışın. Bu iki stringin farklı şekillerde bildirildiğini görebilirsiniz. Biri yeni operatörle, diğeri onsuz. Nedeni burada yatıyor. New operatörü bir nesne oluşturmak için kullanıldığında , nesne için zorla yeni bir bellek alanı ayırır. Ve new kullanılarak oluşturulan bir dize, dize havuzunda son bulmaz - metni, dize havuzundaki bir dizeyle mükemmel bir şekilde eşleşse bile ayrı bir nesne haline gelir. Yani aşağıdaki kodu yazarsak;
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
String s3 = new String("CodeGym is the best website for learning Java!");
}
}
Bellekte şöyle görünür:
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = new String("CodeGym is the best website for learning Java!");
System.out.println(s1.equals(s2));
}
}
Konsol çıktısı: gerçek İlginç. s1 ve s2'nin bellekte farklı alanlara işaret ettiğinden eminiz . Ancak equals() yöntemi bize hala eşit olduklarını söyler. Neden? Daha önce, nesneleri istediğimiz gibi karşılaştırmak için equals() yönteminin geçersiz kılınabileceğini söylediğimizi hatırlıyor musunuz ? String sınıfıyla yaptıkları tam da buydu . Eşittir() işlevini geçersiz kılaryöntem. Ve referansları karşılaştırmak yerine, dizelerdeki karakter sırasını karşılaştırır. Metin aynıysa, nasıl yaratıldıkları veya nerede saklandıkları önemli değildir: dize havuzunda veya ayrı bir bellek alanında. Karşılaştırmanın sonucu doğru olacaktır. Bu arada Java, büyük/küçük harfe duyarsız dize karşılaştırmaları yapmanıza izin verir. Normal olarak, dizelerden birinin tamamı büyük harflerden oluşuyorsa, karşılaştırmanın sonucu yanlış olacaktır:
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = new String("CODEGYM IS THE BEST WEBSITE FOR LEARNING JAVA!");
System.out.println(s1.equals(s2));
}
}
Konsol çıktısı: false Büyük/küçük harfe duyarsız karşılaştırmalar için, String sınıfı equalsIgnoreCase() yöntemine sahiptir . Harf durumu yerine yalnızca belirli karakterlerin sırasını karşılaştırmayı önemsiyorsanız kullanabilirsiniz. Örneğin, bu, iki adresi karşılaştırırken yardımcı olabilir:
public class Main {
public static void main(String[] args) {
String address1 = "2311 Broadway Street, San Francisco";
String address2 = new String("2311 BROADWAY STREET, SAN FRANCISCO");
System.out.println(address1.equalsIgnoreCase(address2));
}
}
Bu durumda, açıkça aynı adresten bahsediyoruz, bu nedenle equalsIgnoreCase() yöntemini kullanmak mantıklıdır.
String.intern() yöntemi
String sınıfının bir hileli yöntemi daha vardır: intern() ; intern () yöntemi, doğrudan dize havuzuyla çalışır. Bir dizide intern() yöntemini çağırırsanız :- Dize havuzunda eşleşen bir dize olup olmadığını kontrol eder.
- Varsa, havuzdaki dizgeye referans döndürür.
- Değilse, dizeyi dize havuzuna ekler ve ona bir başvuru döndürür.
public class Main {
public static void main(String[] args) {
String s1 = "CodeGym is the best website for learning Java!";
String s2 = new String("CodeGym is the best website for learning Java!");
System.out.println(s1 == s2.intern());
}
}
Konsol çıktısı: true Bu dizeleri daha önce intern() olmadan karşılaştırdığımızda sonuç yanlıştı. Şimdi intern() yöntemi , "CodeGym Java öğrenmek için en iyi site!" dizesinin olup olmadığını kontrol eder. dizi havuzundadır. Tabii ki: ile yarattık
String s1 = "CodeGym is the best website for learning Java!";
s1 ve s2.intern() tarafından döndürülen referansın aynı bellek alanını gösterip göstermediğini kontrol ederiz . Ve tabii ki yaparlar :) Özetle, bu önemli kuralı ezberleyin ve uygulayın: Dizeleri karşılaştırmak için DAİMA equals() yöntemini kullanın! Dizeleri karşılaştırırken, neredeyse her zaman referansları, bellek alanlarını veya başka herhangi bir şeyi değil karakterlerini karşılaştırmayı kastediyoruz. equals () yöntemi tam olarak ihtiyacınız olanı yapar. Öğrendiklerinizi pekiştirmek için Java Kursumuzdan bir video dersi izlemenizi öneririz.
Daha fazla okuma: |
---|
GO TO FULL VERSION