JavaRush /Java blogi /Random-UZ /Ob'ektlarni taqqoslash: amaliyot
articles
Daraja

Ob'ektlarni taqqoslash: amaliyot

Guruhda nashr etilgan
Bu ob'ektlarni taqqoslashga bag'ishlangan ikkinchi maqola. Ulardan birinchisi, taqqoslashning nazariy asoslari - u qanday amalga oshiriladi, nima uchun va qayerda qo'llaniladi. Ushbu maqolada biz to'g'ridan-to'g'ri raqamlar, ob'ektlar, maxsus holatlar, nozikliklar va aniq bo'lmagan fikrlarni taqqoslash haqida gapiramiz. Aniqrog'i, biz nima haqida gaplashamiz:
Ob'ektlarni taqqoslash: amaliyot - 1
  • Satrni taqqoslash: ' ==' vaequals
  • UsulString.intern
  • Haqiqiy ibtidoiylarni solishtirish
  • +0.0Va-0.0
  • Ma'nosiNaN
  • Java 5.0. ==Yaratish usullari va ' ' orqali taqqoslash
  • Java 5.0. Autoboxing/Unboxing: ' ==', ' >=' va ' <=' ob'ektni o'rash uchun.
  • Java 5.0. enum elementlarini taqqoslash (turi enum)
Shunday ekan, boshlaylik!

Satrni taqqoslash: ' ==' vaequals

Oh, bu chiziqlar ... Eng ko'p ishlatiladigan turlardan biri, bu juda ko'p muammolarni keltirib chiqaradi. Aslida, ular haqida alohida maqola mavjud . Va bu erda men taqqoslash masalalariga to'xtalaman. Albatta, satrlarni yordamida solishtirish mumkin equals. Bundan tashqari, ular orqali solishtirish kerak equals. Biroq, bilishga arziydigan nozikliklar mavjud. Birinchidan, bir xil satrlar aslida bitta ob'ektdir. Buni quyidagi kodni ishga tushirish orqali osongina tekshirish mumkin:
String str1 = "string";
String str2 = "string";
System.out.println(str1==str2 ? "the same" : "not the same");
Natija "bir xil" bo'ladi . Bu shuni anglatadiki, satr havolalari teng. Bu xotirani tejash uchun kompilyator darajasida amalga oshiriladi. Kompilyator satrning BIR nusxasini yaratadi va ushbu misolga havola str1tayinlaydi . str2Biroq, bu faqat kodda literal sifatida e'lon qilingan satrlarga tegishli. Agar siz bo'laklardan satr tuzsangiz, unga havola boshqacha bo'ladi. Tasdiqlash - bu misol:
String str1 = "string";
String str2 = "str";
String str3 = "ing";
System.out.println(str1==(str2+str3) ? "the same" : "not the same");
Natija "bir xil emas" bo'ladi . Shuningdek, nusxa ko'chirish konstruktori yordamida yangi ob'ekt yaratishingiz mumkin:
String str1 = "string";
String str2 = new String("string");
System.out.println(str1==str2 ? "the same" : "not the same");
Natija ham "bir xil emas" bo'ladi . Shunday qilib, ba'zan satrlarni mos yozuvlar taqqoslash orqali solishtirish mumkin. Ammo bunga ishonmaslik yaxshiroqdir. Men bir qatorning kanonik tasvirini olish imkonini beruvchi juda qiziqarli usulga to'xtalib o'tmoqchiman - String.intern. Keling, bu haqda batafsilroq gaplashaylik.

String.intern usuli

StringKeling, sinf string pulni qo'llab-quvvatlashidan boshlaylik . Sinflarda aniqlangan barcha satr harflari, nafaqat ular, bu hovuzga qo'shiladi. Shunday qilib, usul internushbu hovuzdan mavjud bo'lgan (usul chaqirilgan intern) nuqtai nazaridan teng bo'lgan satrni olish imkonini beradi equals. Agar hovuzda bunday qator bo'lmasa, u erda mavjud bo'lgan joy qo'yiladi va unga havola qaytariladi. Shunday qilib, ikkita teng satrga havolalar har xil bo'lsa ham (yuqoridagi ikkita misolda bo'lgani kabi), bu satrlarga qo'ng'iroqlar internbir xil ob'ektga havolani qaytaradi:
String str1 = "string";
String str2 = new String("string");
System.out.println(str1.intern()==str2.intern() ? "the same" : "not the same");
Ushbu kod qismini bajarish natijasi "bir xil" bo'ladi . Nima uchun bunday qilinganini aniq ayta olmayman. Usul internmahalliy va rostini aytsam, men C kodining yirtqichlariga kirishni xohlamayman. Ehtimol, bu xotira sarfini va ish faoliyatini optimallashtirish uchun qilingan. Har holda, ushbu amalga oshirish xususiyati haqida bilishga arziydi. Keling, keyingi qismga o'tamiz.

Haqiqiy ibtidoiylarni solishtirish

Boshlash uchun men bir savol bermoqchiman. Juda oddiy. Quyidagi yig'indi nima - 0,3f + 0,4f? Nega? 0,7f? Keling, tekshiramiz:
float f1 = 0.7f;
float f2 = 0.3f + 0.4f;
System.out.println("f1==f2: "+(f1==f2));
Natijada? Qani? Menga ham. Ushbu parchani tugatmaganlar uchun aytamanki, natija ...
f1==f2: false
Nima uchun bu sodir bo'lmoqda?.. Keling, yana bir sinov o'tkazamiz:
float f1 = 0.3f;
float f2 = 0.4f;
float f3 = f1 + f2;
float f4 = 0.7f;
System.out.println("f1="+(double)f1);
System.out.println("f2="+(double)f2);
System.out.println("f3="+(double)f3);
System.out.println("f4="+(double)f4);
ga aylantirilishiga e'tibor bering double. Bu ko'proq o'nli kasrlarni chiqarish uchun amalga oshiriladi. Natija:
f1=0.30000001192092896
f2=0.4000000059604645
f3=0.7000000476837158
f4=0.699999988079071
To'g'ri aytganda, natijani oldindan aytish mumkin. Kasr qismini tasvirlash 2-n sonli qator yordamida amalga oshiriladi va shuning uchun o'zboshimchalik bilan tanlangan sonning aniq ko'rinishi haqida gapirishning hojati yo'q. Misoldan ko'rinib turibdiki, vakillik aniqligi float7 kasrdan iborat. To'g'ri aytganda, vakillik float mantisaga 24 bit ajratadi. Shunday qilib, yordamida ifodalanishi mumkin bo'lgan minimal mutlaq son float (darajani hisobga olmagan holda, chunki biz aniqlik haqida gapiramiz) 2-24≈6*10-8. Aynan shu bosqichda vakillikdagi qiymatlar haqiqatda ketadi float. Va kvantlash mavjud bo'lganligi sababli, xato ham bor. Demak, xulosa: vakillikdagi raqamlarni floatfaqat ma'lum bir aniqlik bilan solishtirish mumkin. Men ularni 6-o'nlik kasrga (10-6) yaxlitlashni yoki, yaxshisi, ular orasidagi farqning mutlaq qiymatini tekshirishni tavsiya qilaman:
float f1 = 0.3f;
float f2 = 0.4f;
float f3 = f1 + f2;
float f4 = 0.7f;
System.out.println("|f3-f4|<1e-6: "+( Math.abs(f3-f4) < 1e-6 ));
Bunday holda, natija quvonarli:
|f3-f4|<1e-6: true
Albatta, rasm turi bilan mutlaqo bir xil double. Yagona farq shundaki, mantis uchun 53 bit ajratilgan, shuning uchun tasvirning aniqligi 2-53≈10-16. Ha, kvantlash qiymati ancha kichik, lekin u mavjud. Va u shafqatsiz hazil o'ynashi mumkin. Aytgancha, JUnit test kutubxonasida haqiqiy sonlarni solishtirish usullarida aniqlik aniq ko'rsatilgan. Bular. taqqoslash usuli uchta parametrni o'z ichiga oladi - raqam, u nimaga teng bo'lishi kerak va taqqoslashning aniqligi. Aytgancha, darajani ko'rsatib, ilmiy formatda raqamlarni yozish bilan bog'liq nozikliklarni eslatib o'tmoqchiman. Savol. 10-6 qanday yoziladi? Amaliyot shuni ko'rsatadiki, 80% dan ortiq javob - 10e-6. Ayni paytda, to'g'ri javob 1e-6! Va 10e-6 - 10-5! Biz kutilmaganda loyihalardan birida ushbu rakega qadam qo'ydik. Ular xatoni juda uzoq vaqt qidirdilar, konstantalarga 20 marta qaradilar.Va ularning to'g'riligiga hech kimda shubha yo'q edi, to bir kuni, asosan tasodifan, doimiy 10e-3 chop etilib, ikkitasini topdilar. kutilgan uchta o'rniga kasrdan keyingi raqamlar. Shuning uchun, ehtiyot bo'ling! Keling, davom etaylik.

+0,0 va -0,0

Haqiqiy sonlarni ko'rsatishda eng muhim bit imzolanadi. Boshqa barcha bitlar 0 bo'lsa nima bo'ladi? Butun sonlardan farqli o'laroq, natija ko'rsatish oralig'ining pastki chegarasida joylashgan manfiy son bo'lsa, faqat eng muhim bit 1 ga o'rnatilgan haqiqiy son faqat minus belgisi bilan 0 ni bildiradi. Shunday qilib, bizda ikkita nol bor - +0,0 va -0,0. Mantiqiy savol tug'iladi: bu raqamlarni teng deb hisoblash kerakmi? Virtual mashina aynan shunday fikrda. Biroq, bu ikki xil raqam, chunki ular bilan operatsiyalar natijasida turli xil qiymatlar olinadi:
float f1 = 0.0f/1.0f;
float f2 = 0.0f/-1.0f;
System.out.println("f1="+f1);
System.out.println("f2="+f2);
System.out.println("f1==f2: "+(f1==f2));
float f3 = 1.0f / f1;
float f4 = 1.0f / f2;
System.out.println("f3="+f3);
System.out.println("f4="+f4);
... va natija:
f1=0.0
f2=-0.0
f1==f2: true
f3=Infinity
f4=-Infinity
Shunday qilib, ba'zi hollarda +0,0 va -0,0 ni ikki xil raqam sifatida ko'rib chiqish mantiqiy. Va agar bizda ikkita ob'ekt bo'lsa, ulardan birida maydon +0,0, ikkinchisida -0,0 bo'lsa, bu ob'ektlarni ham tengsiz deb hisoblash mumkin. Savol tug'iladi - agar ularni virtual mashina bilan to'g'ridan-to'g'ri taqqoslash beradigan bo'lsa, raqamlar teng emasligini qanday tushunish mumkin true? Javob bu. Virtual mashina bu raqamlarni teng deb hisoblasa ham, ularning ko'rinishlari hali ham farq qiladi. Shuning uchun, qilish mumkin bo'lgan yagona narsa - qarashlarni solishtirish. Va uni olish uchun shaklda va mos ravishda bir oz ko'rinishni qaytaradigan usullar mavjud int Float.floatToIntBits(float)va (oldingi misolning davomi): long Double.doubleToLongBits(double)intlong
int i1 = Float.floatToIntBits(f1);
int i2 = Float.floatToIntBits(f2);
System.out.println("i1 (+0.0):"+ Integer.toBinaryString(i1));
System.out.println("i2 (-0.0):"+ Integer.toBinaryString(i2));
System.out.println("i1==i2: "+(i1 == i2));
Natija bo'ladi
i1 (+0.0):0
i2 (-0.0):10000000000000000000000000000000
i1==i2: false
Shunday qilib, agar sizda +0,0 va -0,0 turli raqamlar bo'lsa, haqiqiy o'zgaruvchilarni ularning bit ko'rinishi orqali solishtirishingiz kerak. Biz +0.0 va -0.0 ni ajratganga o'xshaymiz. -0,0, ammo yagona ajablanib emas. Shu kabi narsa ham bor ...

NaN qiymati

NaNuchun turadi Not-a-Number. Bu qiymat noto'g'ri matematik operatsiyalar natijasida paydo bo'ladi, masalan, 0,0 ni 0,0 ga, cheksizlikni cheksizga bo'lish va hokazo. Bu qiymatning o'ziga xosligi shundaki, u o'ziga teng emas. Bular.:
float x = 0.0f/0.0f;
System.out.println("x="+x);
System.out.println("x==x: "+(x==x));
... natija beradi ...
x=NaN
x==x: false
Ob'ektlarni solishtirganda bu qanday bo'lishi mumkin? Agar ob'ektning maydoni ga teng bo'lsa NaN, u holda taqqoslash beradi false, ya'ni. ob'ektlarni tengsiz deb hisoblash kafolatlanadi. Garchi, mantiqan, biz buning aksini xohlashimiz mumkin. Usul yordamida kerakli natijaga erishishingiz mumkin Float.isNaN(float). trueArgument bo'lsa, qaytariladi NaN. Bunday holda, men bit ko'rinishlarini solishtirishga tayanmayman, chunki u standartlashtirilmagan. Ehtimol, bu ibtidoiylar haqida etarli. Endi Java-da 5.0 versiyasidan beri paydo bo'lgan nozikliklarga o'tamiz. Va men to'xtalmoqchi bo'lgan birinchi nuqta

Java 5.0. ==Yaratish usullari va ' ' orqali taqqoslash

Dizaynda ishlab chiqarish usuli deb ataladigan naqsh mavjud. Ba'zan uni ishlatish konstruktordan foydalanishdan ko'ra ancha foydali bo'ladi. Sizga bir misol keltiraman. O'ylaymanki, men ob'ekt qobig'ini yaxshi bilaman Boolean. Bu sinf o'zgarmas va faqat ikkita qiymatdan iborat bo'lishi mumkin. Ya'ni, aslida, har qanday ehtiyojlar uchun faqat ikkita nusxa etarli. Va agar siz ularni oldindan yaratsangiz va keyin ularni oddiygina qaytarsangiz, bu konstruktordan foydalanishdan ko'ra tezroq bo'ladi. Bunday usul mavjud Boolean:. valueOf(boolean)U 1.4 versiyasida paydo bo'ldi. Shunga o'xshash ishlab chiqarish usullari 5.0 versiyasida Byte, Character, Short, Integerva sinflarda joriy etilgan Long. Ushbu sinflar yuklanganda, ularning misollarining massivlari ibtidoiy qiymatlarning ma'lum diapazonlariga mos keladi. Bu diapazonlar quyidagicha:
Ob'ektlarni taqqoslash: amaliyot - 2
Bu shuni anglatadiki, usuldan foydalanganda, valueOf(...)agar argument belgilangan diapazonga tushsa, har doim bir xil ob'ekt qaytariladi. Ehtimol, bu tezlikni biroz oshirish imkonini beradi. Ammo shu bilan birga, muammolar shunday tabiatda paydo bo'ladiki, uning tubiga borish juda qiyin bo'lishi mumkin. Bu haqda ko'proq o'qing. Nazariy jihatdan ishlab chiqarish usuli ham sinflarga , ham sinflarga valueOfqo'shilgan . Ularning tavsifida aytilishicha, agar sizga yangi nusxa kerak bo'lmasa, unda bu usuldan foydalanish yaxshiroqdir, chunki tezlikni oshirishi mumkin va hokazo. va h.k. Biroq, joriy (Java 5.0) amalga oshirishda ushbu usulda yangi namuna yaratiladi, ya'ni. Uning ishlatilishi tezlikni oshirishi kafolatlanmaydi. Bundan tashqari, men uchun bu usulni qanday tezlashtirish mumkinligini tasavvur qilish qiyin, chunki qadriyatlarning uzluksizligi tufayli u erda keshni tashkil qilib bo'lmaydi. Butun sonlardan tashqari. Aytmoqchimanki, kasr qismisiz.FloatDouble

Java 5.0. Autoboxing/Unboxing: ' ==', ' >=' va ' <=' ob'ektni o'rash uchun.

Ishlab chiqarish usullari va misol keshi operatsiyalarni optimallashtirish uchun butun sonli primitivlar uchun o'ramlarga qo'shilgan deb o'ylayman autoboxing/unboxing. Bu nima ekanligini eslatib o'taman. Agar ob'ekt operatsiyada ishtirok etishi kerak bo'lsa, lekin primitiv ishtirok etsa, u holda bu primitiv avtomatik ravishda ob'ekt o'ramiga o'raladi. Bu autoboxing. Va aksincha - agar operatsiyada ibtidoiy ishtirok etishi kerak bo'lsa, unda siz u erda ob'ekt qobig'ini almashtirishingiz mumkin va qiymat undan avtomatik ravishda kengaytiriladi. Bu unboxing. Tabiiyki, bunday qulaylik uchun pul to'lashingiz kerak. Avtomatik konvertatsiya operatsiyalari dasturni biroz sekinlashtiradi. Biroq, bu hozirgi mavzuga tegishli emas, shuning uchun bu savolni qoldiraylik. Primitivlar yoki qobiqlar bilan aniq bog'liq bo'lgan operatsiyalar bilan shug'ullanar ekanmiz, hamma narsa yaxshi. "" operatsiyasi nima bo'ladi ==? Aytaylik, bizda Integerbir xil qiymatga ega ikkita ob'ekt bor. Ularni qanday solishtirishadi?
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println("i1==i2: "+(i1==i2));
Natija:
i1==i2: false

Кто бы сомневался... Сравниваются они How an objectы. А если так:Integer i1 = 1;
Integer i2 = 1;
System.out.println("i1==i2: "+(i1==i2));
Natija:
i1==i2: true
Endi bu qiziqroq! Agar autoboxing-e bir xil ob'ektlar qaytarilsa! Bu erda tuzoq yotadi. Xuddi shu ob'ektlar qaytarilganligini aniqlaganimizdan so'ng, bu har doim shundaymi yoki yo'qligini tekshirishni boshlaymiz. Va qancha qiymatni tekshiramiz? Birmi? O'n? Yuz? Katta ehtimol bilan biz nol atrofida har bir yo'nalishda o'zimizni yuzta bilan cheklaymiz. Va biz hamma joyda tenglikka erishamiz. Hamma narsa yaxshi bo'lganga o'xshaydi. Biroq, bir oz orqaga qarang, bu erda . Tutqich nima ekanligini taxmin qildingizmi?.. Ha, avtoboks paytida ob'ekt qobig'ining misollari ishlab chiqarish usullari yordamida yaratilgan. Buni quyidagi test yaxshi ko'rsatadi:
public class AutoboxingTest {

    private static final int numbers[] = new int[]{-129,-128,127,128};

    public static void main(String[] args) {
        for (int number : numbers) {
            Integer i1 = number;
            Integer i2 = number;
            System.out.println("number=" + number + ": " + (i1 == i2));
        }
    }
}
Natija shunday bo'ladi:
number=-129: false
number=-128: true
number=127: true
number=128: false
Keshlash oralig'iga kiruvchi qiymatlar uchun bir xil ob'ektlar qaytariladi, undan tashqarida bo'lganlar uchun esa turli xil ob'ektlar qaytariladi. Va shuning uchun, agar dasturning biron bir joyida primitivlar o'rniga qobiqlar solishtirilsa, eng dahshatli xatoga yo'l qo'yish ehtimoli bor: suzuvchi. Chunki kod, ehtimol, ushbu xato paydo bo'lmaydigan cheklangan qiymatlar oralig'ida ham sinovdan o'tkaziladi. Ammo haqiqiy ishda u ba'zi hisob-kitoblar natijalariga qarab paydo bo'ladi yoki yo'qoladi. Bunday xatoni topishdan ko'ra aqldan ozish osonroq. Shuning uchun, iloji boricha avtoboksdan qochishingizni maslahat beraman. Va bu emas. Keling, matematikani eslaylik, 5-sinfdan tashqari. Tengsizliklar A>=Bva А<=B. O'zaro munosabatlar haqida nima deyish mumkin Ava B? Faqat bitta narsa bor - ular teng. To'g'rimi? Menimcha ha. Keling, testni bajaramiz:
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println("i1>=i2: "+(i1>=i2));
System.out.println("i1<=i2: "+(i1<=i2));
System.out.println("i1==i2: "+(i1==i2));
Natija:
i1>=i2: true
i1<=i2: true
i1==i2: false
Va bu men uchun eng katta g'alati narsa. Agar shunday qarama-qarshiliklarni keltirib chiqarsa, nima uchun bu xususiyat tilga kiritilganini umuman tushunmayman. Umuman olganda, men yana bir bor takrorlayman - agar u holda qilish mumkin bo'lsa autoboxing/unboxing, unda bu imkoniyatdan to'liq foydalanishga arziydi. Men to'xtalib o'tmoqchi bo'lgan oxirgi mavzu... Java 5.0. ro'yxatga olish elementlarini solishtirish (enum turi) Ma'lumki, Java 5.0 versiyasidan boshlab enum - enumeration kabi turni kiritdi. Uning namunalari sukut bo'yicha sinfdagi misol deklaratsiyasida nom va tartib raqamini o'z ichiga oladi. Shunga ko'ra, e'lon qilish tartibi o'zgarganda, raqamlar o'zgaradi. Biroq, "Serializatsiya qanday bo'lsa," maqolasida aytganimdek , bu muammo tug'dirmaydi. Barcha ro'yxatga olish elementlari bitta nusxada mavjud, bu virtual mashina darajasida boshqariladi. Shuning uchun ularni havolalar yordamida to'g'ridan-to'g'ri solishtirish mumkin. * * * Ehtimol, bugungi kunda ob'ektlarni taqqoslashni amalga oshirishning amaliy tomoni haqida hamma narsa shu. Ehtimol, men nimanidir sog'indim. Har doimgidek, sharhlaringizni kutaman! Hozircha ruxsat beraman. E'tiboringiz uchun barchangizga rahmat! Manbaga havola: Ob'ektlarni taqqoslash: amaliyot
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION