equals
жана hashCode
бул эки ыкманы тең өз класстарында ырааттуу түрдө жокко чыгаруу максатка ылайыктуу. Бир аз азыраак сан эмне үчүн мындай болгонун жана бул эреже бузулса, кандай кайгылуу кесепеттерге алып келиши мүмкүн экенин билет. Мен бул ыкмалардын концепциясын карап чыгууну, алардын максатын кайталоону жана эмне үчүн мынчалык тыгыз байланышта экенин түшүнүүнү сунуштайм. Мен бул макаланы, класстарды жүктөө жөнүндө мурункудай эле, маселенин бардык чоо-жайын ачып берүү жана үчүнчү тараптын булактарына кайрылбоо үчүн өзүм үчүн жаздым. Ошондуктан мен конструктивдүү сынга ыраазы болом, анткени бир жерде боштуктар болсо, аларды жоюу керек. Макала, тилекке каршы, абдан узун болуп чыкты.
жокко чыгаруу эрежелерине барабар
Бир эле келип чыккан эки an object логикалык жактан бирдейequals()
экендигин тастыктоо же четке кагуу үчүн Javaда метод талап кылынат . Башкача айтканда, эки an objectти салыштырып жатканда, программист алардын маанилүү талаалары эквиваленттүү экендигин түшүнүшү керек . Бардык талаалар бирдей болушу шарт эмес, анткени метод логикалык теңчorкти билдирет . Бирок кээде бул ыкманы колдонуунун өзгөчө зарылдыгы жок. Алар айткандай, белгилүү бир механизмди колдонуу менен көйгөйлөрдөн качуунун эң оңой жолу - аны колдонбоо. Ошондой эле белгилей кетүү керек, сиз келишимди бузгандан кийин, сиз башка an objectтер жана структуралар сиздин an objectиңиз менен кандайча өз ара аракеттенишерин түшүнбөй каласыз. Анан катанын себебин табуу абдан кыйын болот. equals()
equals
Бул ыкманы жокко чыгаруу үчүн эмес
- Класстын ар бир мисалы уникалдуу болгондо. Көбүрөөк даражада бул маалыматтар менен иштөө үчүн эмес, конкреттүү жүрүм-турумду камсыз кылган класстарга тиешелүү. Мындай, мисалы, класс
- Чындыгында класстан анын инстанцияларынын эквиваленттүүлүгүн аныктоо талап кылынбаганда. Мисалы, класс үчүн
- Сиз кеңейтип жаткан класстын өзүнүн ыкмасын ишке ашырууга ээ болгондо
equals
жана бул ишке ашыруунун жүрүм-туруму сизге ылайыктуу. Мисалы, класстар үчүн equals
Акыр-аягы, классыңыздын масштабы качанprivate
же болгондо жокко чыгаруунун кереги жокpackage-private
жана бул ыкма эч качан чакырылbyte деп ишенесиз.
Thread
. Алар үчүн equals
класс тарабынан берилген ыкманы ишке ашыруу Object
жетиштүү. Дагы бир мисал - энум класстары ( Enum
).
java.util.Random
класстын инстанцияларын бири-бири менен салыштыруунун эч кандай кажети жок, алар кокус сандардын бирдей ырааттуулугун кайтара алабы же жокпу аныктоо. Жөн гана, анткени бул класстын табияты мындай жүрүм-турумду билдирбейт.
Set
, ишке ашыруу , жана List
тиешелүүлүгүнө жараша болот . Map
equals
AbstractSet
AbstractList
AbstractMap
келишимге барабар
Методду жокко чыгарууда,equals
иштеп чыгуучу Java тorнин спецификациясында аныкталган негизги эрежелерди сакташы керек.
- Рефлексивдүүлүк ар кандай берилген маани үчүн
- Симметрия ар кандай берилген баалуулуктар үчүн
- Өтмөлүк ар кандай берилген баалуулуктар үчүн жана
- ырааттуулук ар кандай берилген баалуулуктар үчүн
- Салыштыруу null кандайдыр бир маани үчүн
x
туюнтма x.equals(x)
кайтып келиши керек true
.
Берилген - ушундай дегенди билдирет
x != null
x
жана y
, эгерде ал кайтып келсе гана x.equals(y)
кайтарылышы керек . true
y.equals(x)
true
x
, эгерде кайтарып берсе жана кайтарса , маанини кайтарышы керек . y
z
x.equals(y)
true
y.equals(z)
true
x.equals(z)
true
x
жана y
кайталанган чакыруу x.equals(y)
эки an objectти салыштыруу үчүн колдонулган талаалар чалуулардын ортосунда өзгөрбөсө, бул ыкмага мурунку чалуунун маанисин кайтарат.
x
чалуу x.equals(null)
кайтып келиши керек false
.
келишимди бузууга барабар
Java Collections Framework сыяктуу көптөгөн класстар методдун ишке ашырылышына көз карандыequals()
, андыктан ага көңүл бурбай коюуга болбойт, анткени Бул ыкманын келишимин бузуу өтүнмөнүн акылга сыйбаган иштешине алып келиши мүмкүн, жана бул учурда анын себебин табуу абдан кыйын болот. Рефлексивдүүлүк принцибине ылайык , ар бир an object өзүнө эквиваленттүү болушу керек. Эгерде бул принцип бузулса, коллекцияга an objectти кошуп, анан аны метод аркылуу издегенде, contains()
коллекцияга жаңы эле кошкон an objectти таба албайбыз. Симметрия шарты эки an objectтин салыштырылган тартибине карабастан бирдей болушу керек экенин айтат. equals
Мисалы, сизде сап түрүнүн бир гана талаасын камтыган классыңыз болсо, бул талааны методдогу сап менен салыштыруу туура эмес болот . Анткени тескери салыштыруу болгон учурда, ыкма ар дайым маанини кайтарып берет false
.
// Нарушение симметричности
public class SomeStringify {
private String s;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof SomeStringify) {
return s.equals(((SomeStringify) o).s);
}
// нарушение симметричности, классы разного происхождения
if (o instanceof String) {
return s.equals(o);
}
return false;
}
}
//Правильное определение метода equals
@Override
public boolean equals(Object o) {
if (this == o) return true;
return o instanceof SomeStringify &&
((SomeStringify) o).s.equals(s);
}
Өтмөлүк шартынан келип чыгат, эгерде үч an objectтин экөө тең болсо, анда бул учурда үчөө тең бирдей болушу керек. Белгилүү бир базалык класска маанилүү компонентти кошуу менен кеңейтүү зарыл болгондо, бул принцип оңой бузулушу мүмкүн . Point
Мисалы, координаттары бар класска x
жана y
аны кеңейтүү менен чекиттин түсүн кошуу керек. ColorPoint
Бул үчүн, тиешелүү талаа менен классты жарыялоо керек болот color
. Ошентип, эгерде кеңейтилген класста биз equals
ата-эне ыкмасын чакырсак, ал эми ата-энеде биз координаттар гана x
жана салыштырылат деп ойлосок y
, анда ар кандай түстөгү, бирок координаталары бирдей болгон эки чекит бирдей деп эсептелет, бул туура эмес. Бул учурда туунду классты түстөрдү айырмалоого үйрөтүү зарыл. Бул үчүн, сиз эки ыкманы колдоно аласыз. Бирок бири симметрия эрежесин бузса , экинчиси транзиттик .
// Первый способ, нарушая симметричность
// Метод переопределен в классе ColorPoint
@Override
public boolean equals(Object o) {
if (!(o instanceof ColorPoint)) return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
Бул учурда, чакыруу point.equals(colorPoint)
маанисин кайтарат true
, ал эми салыштыруу colorPoint.equals(point)
кайтып келет false
, анткени "өз" классынын an objectисин күтөт. Ошентип, симметрия эрежеси бузулат. Экинчи ыкма чекиттин түсү жөнүндө маалымат жок болгон учурда "сокур" текшерүүнү камтыйт, башкача айтканда, бизде класс бар Point
. Же ал жөнүндө маалымат бар болсо, түстү текшериңиз, башкача айтканда, класстын an objectисин салыштырыңыз ColorPoint
.
// Метод переопределен в классе ColorPoint
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
// Слепая проверка
if (!(o instanceof ColorPoint))
return super.equals(o);
// Полная проверка, включая цвет точки
return super.equals(o) && ((ColorPoint) o).color == color;
}
Бул жерде транзитивдүүлүк принциби төмөнкүдөй бузулат. Төмөнкү an objectтердин аныктамасы бар дейли:
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
Ошентип, теңдик p1.equals(p2)
жана канааттандырылган болсо да p2.equals(p3)
, p1.equals(p3)
ал маанини кайтарат false
. Ошол эле учурда, экинчи ыкма, менин оюмча, азыраак жагымдуу көрүнөт, анткени Кээ бир учурларда, алгоритм сокур болуп, салыштырууну толук аткарбай калышы мүмкүн жана сиз бул тууралуу билбешиңиз мүмкүн. Бир аз поэзия Жалпысынан, мен түшүнгөндөй, бул маселенин конкреттүү чечими жок. Кай Хорстман аттуу авторитеттүү автордун пикири бар, сиз операторду колдонууну an objectтин классын кайтарган instanceof
метод чакырыгы менен алмаштырса болот getClass()
жана an objectтердин өздөрүн салыштырууну баштаардан мурун алардын бир типте экендигине ынануу керек. , жана алардын жалпы келип чыгышынын фактысына көңүл бурушпайт. Ошентип, симметрия жана өтүү эрежелери канааттандырылат. Бирок ошол эле учурда баррикаданын ары жагында кеңири чөйрөдө кем эмес кадыр-барктуу дагы бир автор Жошуа Блох турат, ал бул ыкма Барбара Лисковдун алмаштыруу принцибине каршы келет деп эсептейт. Бул принципте "чалуу codeу базалык класска аны билбей туруп анын субкласстары сыяктуу мамиле кылышы керек " деп айтылат . Ал эми Хорстман сунуш кылган чечимде бул принцип ачык-айкын бузулган, анткени ал ишке ашырууга байланыштуу. Кыскасы, иштин караңгы экени көрүнүп турат. Ошондой эле белгилей кетчү нерсе, Хорстманн өзүнүн ыкмасын колдонуу эрежесин тактап, класстарды түзүүдө стратегияны чечиш керек деп ачык англис тorнде жазат жана эгерде теңдикти тестирлөө суперкласс тарабынан гана жүргүзүлө турган болсо, анда сиз муну аткаруу менен жасай аласыз. операция instanceof
. Болбосо, текшерүүнүн семантикасы туунду класска жараша өзгөргөндө жана методду ишке ашыруу иерархиядан ылдый жылдыруу керек болгондо, сиз методду колдонушуңуз керек getClass()
. Жошуа Блох, өз кезегинде, мурастан баш тартууну жана класска ColorPoint
классты кошуу менен an objectтин курамын колдонууну сунуштайт жана пункт жөнүндө атайын маалымат алуу үчүн Point
мүмкүндүк алуу ыкмасын камсыз кылат . asPoint()
Бул бардык эрежелерди бузуудан качат, бирок, менин оюмча, бул codeду түшүнүүнү кыйындатат. Үчүнчү вариант - IDEди колдонуу менен барабар ыкмасын автоматтык түрдө түзүү. Идея, демек, Horstmann муунун кайталайт, бул ыкманы суперкласста же анын урпактарында ишке ашыруу стратегиясын тандоого мүмкүндүк берет. Акыр-аягы, кийинки ырааттуулук эрежеси an objectтер өзгөрбөсө дагы x
, y
аларды кайра чакыруу x.equals(y)
мурункудай эле маанини кайтарышы керек деп айтылат. Акыркы эреже - эч бир an object тең болбошу керек null
. Бул жерде баары түшүнүктүү null
- бул белгисиздик, an object белгисиздикке барабарбы? Бул түшүнүксүз, false
б.а.
Теңдикти аныктоонун жалпы алгоритми
this
Объектке шилтемелердин жана метод параметрлеринин теңдигин текшериңизo
.if (this == o) return true;
- Шилтеме аныкталганбы же
o
жокпу, текшериңизnull
, б.а.
Эгерде келечекте an objectтин түрлөрүн салыштырганда, оператор колдонула турган болсоinstanceof
, бул элементти өткөрүп жиберүүгө болот, анткени бул параметрfalse
бул учурда кайтып келетnull instanceof Object
. - Оператор же ыкманы
this
колдонуп an objectтин түрлөрүн салыштырыңыз , жогорудагы сүрөттөлүштү жана өзүңүздүн интуицияңызды жетекчorкке алыңыз.o
instanceof
getClass()
- Эгерде ыкма
equals
подкласста жокко чыгарылса, сөзсүз чалыңызsuper.equals(o)
- Параметрдин түрүн
o
керектүү класска айландырыңыз. - Бардык маанилүү an object талааларын салыштыруу:
- примитивдүү түрлөр үчүн (жанадан башка
float
)double
, операторду колдонуу==
- шилтеме талаалары үчүн алардын ыкмасын чакырышыңыз керек
equals
- массивдер үчүн циклдик итерацияны же ыкманы колдонсоңуз болот
Arrays.equals()
- түрлөрү үчүн
float
жанаdouble
тиешелүү ороочу класстардын салыштыруу ыкмаларын колдонуу зарылFloat.compare()
жанаDouble.compare()
- примитивдүү түрлөр үчүн (жанадан башка
- Акырында, үч суроого жооп бериңиз: ишке ашырылган ыкма симметриялуубу ? Transitive ? макулбу ? Калган эки принцип ( рефлексивдүүлүк жана аныктык ) адатта автоматтык түрдө ишке ашырылат.
HashCode эрежелерин жокко чыгаруу
Хэш - бул кандайдыр бир убакта анын абалын сүрөттөгөн an objectтен түзүлгөн сан. Бул сан Java тorнде негизинен хэш tableларда колдонулат, мисалыHashMap
. Мында an objectке негизделген санды алуунун хэш-функциясы хэш tableсы боюнча элементтердин салыштырмалуу бирдей бөлүштүрүлүшүн камсыздай тургандай ишке ашырылышы керек. Ошондой эле функция ар кандай баскычтар үчүн бирдей маанини кайтарганда кагылышуулардын ыктымалдыгын азайтуу үчүн.
Келишимдин hashCode
Хеш-функцияны ишке ашыруу үчүн тил спецификациясы төмөнкү эрежелерди аныктайт:- бир эле an objectте бир же бир нече жолу методду чакыруу,
hashCode
an objectтин маанини эсептөөгө катышкан талаалары өзгөрбөсө, ошол эле хэш маанисин кайтарышы керек. - эки an object боюнча методду чакыруу,
hashCode
эгерде an objectтер бирдей болсо, ар дайым бирдей санды кайтарып бериши керек (equals
бул an objectтер боюнча методду чакыруу кайтаратtrue
). - эки бирдей эмес an object боюнча ыкманы чакыруу
hashCode
ар кандай хэш маанилерин кайтарышы керек. Бул талап милдеттүү эмес болсо да, аны ишке ашыруу хэш tableлардын иштешине оң таасирин тийгизет деп эсептеш керек.
барабар жана hashCode ыкмаларын бирге жокко чыгаруу керек
Жогоруда сүрөттөлгөн келишимдердин негизинде, codeуңуздагы ыкманы жокко чыгаргандаequals
, сиз ар дайым ыкманы жокко чыгарышыңыз керек hashCode
. Чындыгында класстын эки инстанциясы ар кандай эс тутум аймактарында болгондуктан, алар кандайдыр бир логикалык критерийлерге ылайык салыштырылышы керек. Демек, логикалык жактан эквиваленттүү эки an object бирдей хэш маанисин кайтарышы керек. Бул ыкмалардын бирөө гана жокко чыгарылса эмне болот?
-
equals
ообаhashCode
жокequals
Классыбызда методду туура аныктадык дейли жанаhashCode
методду класстагыдай калтырууну чечтикObject
. Анда методдун көз карашы боюнчаequals
эки an object логикалык жактан бирдей болот, ал эми методдун көз карашы боюнчаhashCode
алардын эч кандай жалпылыгы болбойт. Ошентип, an objectти хэш tableга жайгаштыруу менен, биз аны ачкыч менен кайтарып албай калуу коркунучу бар.
Мисалы, бул сыяктуу:Map<Point, String> m = new HashMap<>(); m.put(new Point(1, 1), “Point A”); // pointName == null String pointName = m.get(new Point(1, 1));
Албетте, жайгаштырылып жаткан an object менен изделип жаткан an object логикалык жактан бирдей болгону менен эки башка an object. Бирок Себеби алардын ар кандай хэш баалуулуктары бар, анткени биз келишимди бузгандыктан, биз хэш tableнын ичегинин бир жеринде an objectибизди жоготуп алдык деп айта алабыз.
-
hashCode
ообаequals
жок.Эгерде биз методду жокко чыгарып
hashCode
,equals
класстан методду ишке ашырууну мурастап алсак, эмне болотObject
. Белгилүү болгондой,equals
демейки ыкма жөн гана көрсөткүчтөрдү an objectтерге салыштырып, алардын бир эле an objectке тиешелүү экендигин аныктайт. Келгиле,hashCode
биз методду бардык канондорго ылайык жаздык деп ойлойлу, тактап айтканда, аны IDE аркылуу түздүк жана ал логикалык жактан окшош an objectтер үчүн ошол эле хэш маанилерин кайтарат. Албетте, муну менен биз эки an objectти салыштыруунун кандайдыр бир механизмин аныктадык.Демек, мурунку абзацтагы мисал теориялык жактан аткарылышы керек. Бирок биз дагы эле хэш tableсында an objectибизди таба албайбыз. Биз буга жакын болобуз, анткени, жок дегенде, биз an object жата турган таштанды үстөл себетин табабыз.
Хеш-tableда an objectти ийгorктүү издөө үчүн, ачкычтын хэш маанилерин салыштыруудан тышкары, изделген an object менен ачкычтын логикалык теңдигин аныктоо да колдонулат. Башкача айтканда,
equals
ыкманы жокко чыгарбай туруп, эч кандай жол жок.
hashCode аныктоонун жалпы алгоритми
Бул жерде, менимче, сиз көп кабатыр болбоңуз жана сүйүктүү IDEиңизде ыкманы жаратыңыз. Анткени алтын катышты издөө үчүн биттердин оңго жана солго жылышы, б.а. нормалдуу бөлүштүрүү - бул толугу менен өжөр жигиттер үчүн. Жеке мен бир эле Идеяга караганда жакшыраак жана тезирээк жасай алам деп күмөн санайм.Корутундунун ордуна
Ошентип, биз методдор Java тorнде так аныкталган ролдуequals
ойноорун жана эки an objectтин логикалык теңдигин алуу үчүн түзүлгөнүн көрөбүз. hashCode
Метод болгон учурда, equals
бул an objectтерди салыштырууга түздөн-түз байланышы бар, hashCode
кыйыр учурда, зарыл болгон учурда, айталы, хэш tableларында же окшош маалымат структураларында an objectтин болжолдуу жайгашкан жерин аныктоо үчүн an objectти издөө ылдамдыгын жогорулатуу. Келишимдерден тышкары , an objectтерди салыштырууга байланыштуу дагы бир талап бар equals
. hashCode
Бул compareTo
интерфейс ыкмасынын Comparable
ырааттуулугу equals
. x.equals(y) == true
Бул талап иштеп чыгуучуну ар дайым качан кайтып келүүгө милдеттендирет x.compareTo(y) == 0
. Башкача айтканда, биз эки an objectтин логикалык салыштырылышы өтүнмөнүн эч бир жеринде карама-каршы келбеши керек жана дайыма ырааттуу болушу керек экенин көрөбүз.
GO TO FULL VERSION