JavaRush /Java блогы /Random-KK /Объектілерді салыстыру: тәжірибе
articles
Деңгей

Объектілерді салыстыру: тәжірибе

Топта жарияланған
Бұл an objectілерді салыстыруға арналған мақалалардың екіншісі. Олардың біріншісі салыстырудың теориялық негізін – оның қалай жасалатынын, не үшін және қайда қолданылатынын талқылады. Бұл мақалада біз сандарды, an objectілерді, ерекше жағдайларды, нәзіктіктерді және айқын емес нүктелерді салыстыру туралы тікелей сөйлесеміз. Дәлірек айтқанда, біз не туралы айтатын боламыз:
Объектілерді салыстыру: тәжірибе – 1
  • Жолды салыстыру: ' ==' жәнеequals
  • ӘдісString.intern
  • Нақты примитивтерді салыстыру
  • +0.0Және-0.0
  • МағынасыNaN
  • Java 5.0. ==' ' арқылы әдістерді құру және салыстыру
  • Java 5.0. Autoboxing/Unboxing: ' ==', ' >=' және ' <=' нысан орауыштары үшін.
  • Java 5.0. санау элементтерін салыстыру (түрі enum)
Ендеше, бастайық!

Жолды салыстыру: ' ==' жәнеequals

Әй, мына жолдар... Көбінесе жиі қолданылатын, көп қиындық тудыратын түрлерінің бірі. Негізінде олар туралы бөлек мақала бар . Ал бұл жерде мен салыстыру мәселелеріне тоқталамын. Әрине, жолдарды көмегімен салыстыруға болады equals. Сонымен қатар, олар арқылы салыстыру керек equals. Дегенмен, білуге ​​тұрарлық нәзіктіктер бар. Біріншіден, бірдей жолдар шын мәнінде бір нысан болып табылады. Мұны келесі codeты іске қосу арқылы оңай тексеруге болады:
String str1 = "string";
String str2 = "string";
System.out.println(str1==str2 ? "the same" : "not the same");
Нәтиже «бірдей» болады . Бұл жол сілтемелерінің тең екенін білдіреді. Бұл жадты сақтау үшін компилятор деңгейінде жасалады. Компилятор жолдың БІР данасын жасайды және осы данаға сілтеме str1тағайындайды . str2Дегенмен, бұл codeта литералдар ретінде жарияланған жолдарға ғана қатысты. Егер сіз бөліктерден жол құрастырсаңыз, оған сілтеме басқаша болады. Растау - бұл мысал:
String str1 = "string";
String str2 = "str";
String str3 = "ing";
System.out.println(str1==(str2+str3) ? "the same" : "not the same");
Нәтиже «бірдей емес» болады . Сондай-ақ көшіру конструкторы арқылы жаңа нысан жасауға болады:
String str1 = "string";
String str2 = new String("string");
System.out.println(str1==str2 ? "the same" : "not the same");
Нәтиже де «бірдей емес» болады . Осылайша, кейде жолдарды анықтамалық салыстыру арқылы салыстыруға болады. Бірақ бұған сенбеген дұрыс. Мен жолдың канондық көрінісін алуға мүмкіндік беретін өте қызықты әдіске тоқталғым келеді - String.intern. Бұл туралы толығырақ сөйлесейік.

String.inern әдісі

StringКласс жол пулын қолдайтынынан бастайық . Сыныптарда анықталған барлық жол литералдары, олар ғана емес, осы пулға қосылады. Сонымен, әдіс осы пулдан бар (әдіс деп аталатын ) көзқарас тұрғысынан internтең жолды алуға мүмкіндік береді . Егер бассейнде мұндай жол жоқ болса, онда бар жол сол жерде орналастырылады және оған сілтеме қайтарылады. Осылайша, екі тең жолға сілтемелер әртүрлі болса да (жоғарыдағы екі мысалдағыдай), осы жолдарға шақырулар бір нысанға сілтемені қайтарады: internequalsintern
String str1 = "string";
String str2 = new String("string");
System.out.println(str1.intern()==str2.intern() ? "the same" : "not the same");
Бұл code бөлігін орындау нәтижесі «бірдей» болады . Неліктен бұлай болғанын нақты айта алмаймын. Бұл әдіс internжергілікті және шынымды айтсам, мен C codeының жабайы түрлеріне кіргім келмейді. Бұл жадты тұтынуды және өнімділікті оңтайландыру үшін жасалса керек. Қалай болғанда да, бұл іске асыру мүмкіндігі туралы білу керек. Келесі бөлімге көшейік.

Нақты примитивтерді салыстыру

Бастау үшін мен сұрақ қойғым келеді. Өте қарапайым. Келесі қосынды нешеге тең – 0,3f + 0,4f? Неліктен? 0,7f? Тексерейік:
float f1 = 0.7f;
float f2 = 0.3f + 0.4f;
System.out.println("f1==f2: "+(f1==f2));
Болғандықтан? Ұнады ма? Маған да. Бұл фрагментті аяқтамағандар үшін айтарым, нәтиже...
f1==f2: false
Неліктен бұл болып жатыр?.. Тағы бір сынақ жүргізейік:
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);
түрлендіруге назар аударыңыз double. Бұл ондық бөлшектерді көбірек шығару үшін жасалады. Нәтиже:
f1=0.30000001192092896
f2=0.4000000059604645
f3=0.7000000476837158
f4=0.699999988079071
Дәлірек айтқанда, нәтиже болжауға болады. Бөлшек бөлікті бейнелеу 2-n ақырлы қатарды қолдану арқылы жүзеге асырылады, сондықтан ерікті түрде таңдалған санның нақты көрінісі туралы айтудың қажеті жоқ. Мысалдан көрініп тұрғандай, ұсыну дәлдігі float7 ондық таңба. Қатаң айтқанда, өкілдік float мантиссаға 24 бит бөледі. Осылайша, көмегімен көрсетуге болатын ең аз абсолютті сан float (дәрежені есепке алмағанда, өйткені біз дәлдік туралы айтып отырмыз) 2-24≈6*10-8. Дәл осы қадаммен өкілдіктегі мәндер іс жүзінде жүреді float. Ал кванттау бар болғандықтан, қате де бар. Бұдан шығатын қорытынды: кескіндегі сандарды floatбелгілі бір дәлдікпен ғана салыстыруға болады. Мен оларды 6-ондық белгіге дейін (10-6) дөңгелектеуді немесе олардың арасындағы айырмашылықтың абсолютті мәнін тексеруді ұсынамын :
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 ));
Бұл жағдайда нәтиже қуантады:
|f3-f4|<1e-6: true
Әрине, сурет түрімен бірдей double. Жалғыз айырмашылық мантисса үшін 53 бит бөлінген, сондықтан ұсыну дәлдігі 2-53≈10-16. Иә, кванттау мәні әлдеқайда аз, бірақ ол бар. Және ол қатыгез әзіл ойнай алады. Айтпақшы, JUnit сынақ кітапханасында нақты сандарды салыстыру әдістерінде дәлдік анық көрсетілген. Анау. салыстыру әдісі үш параметрді қамтиды - сан, ол неге тең болуы керек және салыстыру дәлдігі. Айтпақшы, дәрежені көрсете отырып, сандарды ғылыми форматта жазуға байланысты нәзіктіктерді атап өткім келеді. Сұрақ. 10-6 қалай жазылады? Тәжірибе көрсеткендей, 80% -дан астам жауап береді – 10e-6. Сонымен қатар, дұрыс жауап 1e-6! Ал 10e-6 - 10-5! Біз бұл рейске жобалардың бірінде күтпеген жерден қадам бастық. Олар қатені өте ұзақ іздеді, тұрақтыларға 20 рет қарады.Және олардың дұрыстығына ешкім күмәнданбады, бір күні көбінесе кездейсоқ 10e-3 тұрақтысы басып шығарылып, екеуі табылды. күтілетін үштік орнына ондық үтірден кейінгі сандар. Сондықтан, сақ болыңыз! Әрі қарай жүрейік.

+0,0 және -0,0

Нақты сандарды көрсетуде ең маңызды бит таңбаланады. Барлық басқа биттер 0 болса не болады? Мұндай жағдайда нәтиже көрсету диапазонының төменгі шегінде орналасқан теріс сан болатын бүтін сандардан айырмашылығы, тек ең маңызды бит 1-ге орнатылған нақты сан да тек минус таңбасы бар 0-ді білдіреді. Осылайша, бізде екі нөл бар - +0,0 және -0,0. Логикалық сұрақ туындайды: бұл сандарды тең деп санау керек пе? Виртуалды машина дәл осылай ойлайды. Дегенмен, бұл екі түрлі сан, өйткені олармен жұмыс істеу нәтижесінде әртүрлі мәндер алынады:
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);
... және нәтиже:
f1=0.0
f2=-0.0
f1==f2: true
f3=Infinity
f4=-Infinity
Сондықтан кейбір жағдайларда +0,0 және -0,0 екі түрлі сан ретінде қарастыру мағынасы бар. Ал егер бізде екі нысан болса, олардың біреуінде өріс +0,0, ал екіншісінде -0,0 болса, бұл нысандарды да тең емес деп санауға болады. Сұрақ туындайды - егер сандарды виртуалды машинамен тікелей салыстыру беретін болса, олардың тең емес екенін қалай түсінуге болады true? Жауабы мынау. Виртуалды машина бұл сандарды тең деп санаса да, олардың көріністері әлі де әртүрлі. Сондықтан тек көзқарастарды салыстыру ғана мүмкін. Ал оны алу үшін, және int Float.floatToIntBits(float)тиісінше пішіндегі 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));
Нәтиже болады
i1 (+0.0):0
i2 (-0.0):10000000000000000000000000000000
i1==i2: false
Осылайша, егер сізде +0,0 және -0,0 әртүрлі сандар болса, онда нақты айнымалыларды олардың биттік көрінісі арқылы салыстыру керек. Біз +0,0 және -0,0 сұрыптаған сияқтымыз. -0,0, дегенмен, жалғыз тосынсый емес. Сондай-ақ ... сияқты нәрсе бар.

NaN мәні

NaNдегенді білдіреді Not-a-Number. Бұл мән қате математикалық амалдардың нәтижесінде пайда болады, айталық, 0,0-ді 0,0-ге, шексіздікке шексіздікке бөлу және т.б. Бұл құндылықтың ерекшелігі - ол өзіне тең емес. Анау.:
float x = 0.0f/0.0f;
System.out.println("x="+x);
System.out.println("x==x: "+(x==x));
...нәтиже береді...
x=NaN
x==x: false
Бұл нысандарды салыстыру кезінде қалай болуы мүмкін? Егер an objectінің өрісі тең болса NaN, онда салыстыру береді false, яғни. an objectілерді тең емес деп санауға кепілдік беріледі. Дегенмен, логикалық тұрғыдан, біз керісінше болуы мүмкін. Әдістің көмегімен қалаған нәтижеге қол жеткізуге болады Float.isNaN(float). trueАргумент болса, қайтарады NaN. Бұл жағдайда мен бит көріністерін салыстыруға сенбейтін едім, өйткені ол стандартталмаған. Қарабайырлар туралы бұл жеткілікті шығар. Енді Java-да 5.0 нұсқасынан бері пайда болған нәзіктіктерге көшейік. Ал мен бірінші тоқталғым келетін мәселе

Java 5.0. ==' ' арқылы әдістерді құру және салыстыру

Дизайнда өндіру әдісі деп аталатын үлгі бар. Кейде оны пайдалану конструкторды пайдаланудан әлдеқайда тиімді. Бір мысал келтірейін. Менің ойымша, мен an objectінің қабығын жақсы білемін Boolean. Бұл класс өзгермейді және тек екі мәнді қамтуы мүмкін. Яғни, шын мәнінде, кез келген қажеттіліктер үшін екі данасы жеткілікті. Ал егер сіз оларды алдын ала жасап, содан кейін жай ғана қайтарсаңыз, бұл конструкторды пайдаланудан әлдеқайда жылдамырақ болады. Мұндай әдіс бар Boolean: valueOf(boolean). Ол 1.4 нұсқасында пайда болды. Ұқсас өндіру әдістері 5.0 нұсқасында Byte, Character, Short, Integerжәне сыныптарында енгізілген Long. Бұл сыныптар жүктелген кезде олардың даналарының массивтері қарабайыр мәндердің белгілі ауқымдарына сәйкес жасалады. Бұл диапазондар келесідей:
Объектілерді салыстыру: тәжірибе – 2
Бұл әдісті пайдаланған кезде valueOf(...)аргумент көрсетілген ауқымға түссе, сол нысан әрқашан қайтарылатынын білдіреді. Мүмкін бұл жылдамдықты біршама арттыруға мүмкіндік береді. Бірақ сонымен бірге осындай сипаттағы проблемалар туындайды, оның түбіне жету өте қиын болуы мүмкін. Бұл туралы толығырақ оқыңыз. Теорияда өндіру әдісі және valueOfсыныптарына қосылды . Олардың сипаттамасы егер сізге жаңа көшірме қажет болмаса, бұл әдісті қолданған дұрыс, өйткені ол жылдамдықтың жоғарылауын бере алады және т.б. және т.б. Дегенмен, ағымдағы (Java 5.0) іске асыруда бұл әдісте жаңа данасы жасалады, яғни. Оны пайдалану жылдамдықты арттыруға кепілдік бермейді. Оның үстіне, мен үшін бұл әдісті қалай жеделдетуге болатынын елестету қиын, өйткені құндылықтардың үздіксіздігіне байланысты кэшті онда ұйымдастыру мүмкін емес. Бүтін сандарды қоспағанда. Яғни, бөлшек бөлігі жоқ.FloatDouble

Java 5.0. Autoboxing/Unboxing: ' ==', ' >=' және ' <=' нысан орауыштары үшін.

Өндіріс әдістері мен даналық кэш операцияларды оңтайландыру үшін бүтін примитивтерге арналған орауыштарға қосылды деп ойлаймын autoboxing/unboxing. Оның не екенін еске салайын. Егер an object операцияға қатысуы керек болса, бірақ примитив тартылса, онда бұл примитив автоматты түрде нысан орамасына оралады. Бұл autoboxing. Және керісінше - егер операцияға примитив қатысуы керек болса, онда сіз онда an object қабығын ауыстыра аласыз және мән одан автоматты түрде кеңейтіледі. Бұл unboxing. Әрине, мұндай ыңғайлылық үшін төлеуге тура келеді. Автоматты түрлендіру әрекеттері қолданбаны біршама баяулатады. Дегенмен, бұл қазіргі тақырыпқа қатысты емес, сондықтан бұл сұрақты қалдырайық. Қарабайыр немесе қабықшалармен анық байланысты операциялармен айналысатын болсақ, бәрі жақсы. «» операциясы не болады ==? IntegerІшінде бірдей мәні бар екі нысан бар делік . Олар қалай салыстырады?
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println("i1==i2: "+(i1==i2));
Нәтиже:
i1==i2: false

Кто бы сомневался... Сравниваются они How an objectы. А если так:Integer i1 = 1;
Integer i2 = 1;
System.out.println("i1==i2: "+(i1==i2));
Нәтиже:
i1==i2: true
Енді бұл қызықтырақ! Егер autoboxing-e бірдей нысандар қайтарылады! Тұзақ осында жатыр. Бірдей нысандар қайтарылғанын анықтағаннан кейін, бұл әрқашан солай болатынын көру үшін эксперимент жасай бастаймыз. Ал біз қанша мәнді тексереміз? Бір? Он? Жүз? Біз өзімізді нөлге жуық әр бағытта жүзден шектейтін шығармыз. Біз барлық жерде теңдікке ие боламыз. Бәрі жақсы сияқты. Дегенмен, сәл артқа қараңыз, мұнда . Сіз аулаудың не екенін білдіңіз бе?.. Иә, автобоксинг кезінде нысан қабықшаларының даналары өндіру әдістері арқылы жасалады. Мұны келесі сынақ жақсы көрсетеді:
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));
        }
    }
}
Нәтиже келесідей болады:
number=-129: false
number=-128: true
number=127: true
number=128: false
Кэштеу ауқымына түсетін мәндер үшін бірдей нысандар қайтарылады, оның сыртындағылар үшін әртүрлі нысандар қайтарылады. Сондықтан, егер қолданбаның бір жерінде примитивтердің орнына қабықшалар салыстырылса, ең қорқынышты қатені алу мүмкіндігі бар: өзгермелі. Өйткені code бұл қате пайда болмайтын шектеулі мәндер ауқымында да сыналатын болады. Бірақ нақты жұмыста ол кейбір есептеулердің нәтижесіне байланысты пайда болады немесе жоғалады. Мұндай қатені тапқанша, жынды болу оңай. Сондықтан мүмкіндігінше автобоксингтен аулақ болуға кеңес берер едім. Бұл ол емес. Математиканы еске түсірейік, 5-сыныптан жоғары емес. A>=Bжәне теңсіздіктері болсын А<=B. қарым-қатынас туралы не айтуға болады Aжәне B? Бір ғана нәрсе бар - олар тең. Сіз келісесіз бе? Иә деп ойлаймын. Тестті орындайық:
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));
Нәтиже:
i1>=i2: true
i1<=i2: true
i1==i2: false
Ал бұл мен үшін ең үлкен оғаш нәрсе. Мен мұндай қайшылықтарды енгізетін болса, бұл мүмкіндіктің тілге не үшін енгізілгенін мүлдем түсінбеймін. Жалпы, мен тағы бір рет қайталаймын - егер онсыз істеу мүмкін болса autoboxing/unboxing, онда бұл мүмкіндікті барынша пайдалану керек. Мен тоқталғым келетін соңғы тақырып... Java 5.0. санау элементтерін салыстыру (enum түрі) Өздеріңіз білетіндей, Java 5.0 нұсқасынан бастап enum - enumeration сияқты түрін енгізді . Оның даналары әдепкі бойынша сыныптағы дана мәлімдемесіндегі атау мен реттік нөмірді қамтиды. Тиісінше, хабарландыру тәртібі өзгерген кезде сандар да өзгереді. Дегенмен, мен мақалада айтқанымдай , «Серияландыру қалай болса , бұл ешқандай қиындық тудырмайды. Барлық санау элементтері бір көшірмеде бар, бұл виртуалды машина деңгейінде басқарылады. Сондықтан оларды сілтемелер арқылы тікелей салыстыруға болады. * * * Бәлкім, бұл бүгінгі күні нысанды салыстыруды жүзеге асырудың практикалық жағы туралы. Мүмкін мен бірдеңені жіберіп алған шығармын. Әдеттегідей, мен сіздің пікірлеріңізді күтемін! Әзірге демалысымды алуға рұқсат етіңіз. Назарларыңызға барлығыңызға рахмет! Дереккөзге сілтеме: Объектілерді салыстыру: тәжірибе
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION