Метод Equals и метод хэшкода в Java находятся в объекте, поэтому каждый объект имеет эти два метода. Иногда нам нужно внедрить конкретные потребности и, возможно, придется переписать эти два метода. Сегодня мы представим функции этих двух методов.
Методы equals () и hashcode () используются для сравнения в одном и том же классе, особенно при хранении одного и того же объекта класса в контейнере, например, для определения того, дублируется ли размещенный объект.
Здесь нам сначала нужно понять проблему:
Два объекта с равным () равны, hashcode () должен быть равным, а два объекта с равным () не равны, не могут доказать, что их Hashcode () не равны. Другими словами, для двух объектов, чей равенственный () метод не является одинаковым, hashcode () может быть равным. (Насколько я понимаю, вызвано конфликтами хеш -кода при создании)
Здесь HashCode похож на индекс каждого символа в словаре, и Equals () похоже на сравнение разных слов под одним и тем же символом в словаре. Как и в словаре, поиск двух слов «я» и «спонтанно» под словом «я» в словаре, если Equals () используется для определения равенства слова запроса, это то же самое слово. Например, два слова, сравниваемые по равным (), являются «самостоятельно», тогда значения, полученные с помощью метода hashcode (), должны быть равны в настоящее время; Если метод equals () сравнивает слова «я» и «спонтанно», то результат заключается в том, что вы не хотите ждать, но оба эти слова принадлежат словам «я», и поэтому при поиске индексов, то есть hashcode () одинаково. Если Equals () сравнивает слова «self» и «они», то результаты также различны, а результаты, полученные HashCode (), также различаются в настоящее время.
И наоборот: hashcode () отличается, и Equals () может быть введен; HashCode () равен, Equals () может быть равным или не может быть равным. В классе объекта метод hashcode () является локальным методом, который возвращает значение адреса объекта. Метод equals () в классе объекта также сравнивает значения адреса двух объектов. Если equals () равно, это означает, что значения адреса двух объектов также равны, конечно, hashcode () равны;
В то же время алгоритм хэш обеспечивает высокую эффективность для поиска элементов
Если вы хотите узнать, содержится ли объект в коллекции, как вы пишете приблизительный код программы?
Вы обычно вынимаете каждый элемент один за другим, чтобы сравнить с объектом, который вы ищете. Когда вы обнаружите, что результат сравнения метода равных между элементом и объектом, который вы ищете, прекратите поиск и возвращайте положительную информацию. В противном случае вернуть негативную информацию. Если в коллекции есть много элементов, таких как 10 000 элементов, и не содержат объект, который вы ищете, это означает, что вашей программе необходимо взять 10 000 элементов из коллекции и сравнить один за другим, чтобы сделать вывод.
Кто -то изобрел хэш -алгоритм для повышения эффективности поиска элементов из набора. Таким образом, набор разделен на несколько областей хранения. Каждый объект может рассчитать хэш -код, и хеш -код может быть сгруппирован (рассчитывается с использованием различных функций хэш). Каждая группа соответствует определенной области хранения. В соответствии с хэшем объекта он может определить, в какой области, в какой области следует хранить. Внутренне использует метод принятия оставшейся части определенного числа N (эта хеш -функция самая проще) для группировки и разделения хэш -кодов. Класс объекта определяет метод hashcode () для возврата хеш -кода каждого объекта Java. При поиске объекта из коллекции хэшса система Java сначала вызывает метод HashCode () объекта для получения хэш -кодовой таблицы объекта. , затем найдите соответствующую область хранения на основе хэширования и, наконец, получите каждый элемент в области хранения и сравните ее с методом объекта для равных; Таким образом, вы можете сделать вывод, не пройдя все элементы в коллекции. Можно видеть, что коллекция хэшса имеет хорошую производительность поиска объектов, но эффективность хранения объектов в сборе хэшса является относительно низкой, поскольку при добавлении объекта в сбор Hashset вы должны сначала рассчитать хэш -код объекта и определить местоположение хранения объекта в коллекции, основанное на этом хэш -коде. Чтобы гарантировать, что объекты экземпляра класса могут быть нормально храниться в хэшсете, результаты двух экземпляров объектов этого класса также должны быть равны, когда результаты сравниваются с помощью метода evallos () равны; то есть, если результат obj1.equals (obj2) правда, то результат следующего выражения также должно быть истинным:
obj1.hashcode () == obj2.hashcode ()
Другими словами: когда мы переписываем метод равных объекта, мы должны переписать его метод хэшкода. Однако, если мы не переписываем его метод хэшкода, метод хэшкода в объекте объекта всегда возвращает хэш -адрес объекта, и этот адрес никогда не бывает равным. Таким образом, даже если метод Equals переписан в настоящее время, не будет никакого специфического эффекта, потому что, если метод хэшкода не хочет ждать, он не будет вызывать метод равных для сравнения, поэтому он бессмыслен.
Если метод hashcode () класса не соответствует вышеуказанным требованиям, то, когда результаты сравнения между двумя объектами экземпляра этого класса равны методу equals (), их не следует хранить в установленном наборе одновременно. Однако, если они хранятся в наборе хэшса, поскольку возвратное значение их метода HashCode () отличается (возвращаемое значение метода хэшкода в объекте всегда отличается), второй объект может быть помещен в другую область, от первого объекта, сначала в соответствии с расчетом кода хэш, так что он не может, если вы можете сравнить метод Equals с первым объектом, он может быть строится в коллекции HasheSet. Метод HashCode () в классе объекта не может соответствовать требованиям объекта, хранящегося в хэшсете, поскольку его возвратное значение рассчитывается по адресу памяти объекта. Значение хеша, возвращаемое одним и тем же объектом в любое время во время прогона программы, всегда неизменно. Следовательно, до тех пор, пока это два разных объекта экземпляра, даже если результаты сравнения их метода равны равны, возвращаемое значение их метода хэшкода по умолчанию отличается.
Давайте посмотрим на конкретный пример:
Rectobject объект: пакет com.weijia.demo; открытый класс RectObject {public int x; Public int y; public Rectobject (int x, int y) {this.x = x; this.y = y; } @Override public int hashcode () {final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; результат возврата; } @Override public boolean equals (Object obj) {if (this == obj) вернуть true; if (obj == null) вернуть false; if (getClass ()! = obj.getClass ()) вернуть false; окончательный rectobject orhe = (Rectobject) obj; if (x! = Другое.x) {вернуть false; } if (y! = Другое.y) {return false; } вернуть true; }} Мы переопределили методы HashCode и равны в объекте родительского класса и видим, что в методах HashCode и равных, если значения x и y у двух объектов RectObject равны, их значения хэшкода равны и равны возвращаются true;
Вот тестовый код:
пакет com.weijia.demo; импортировать java.util.hashset; открытый класс демонстрация {public static void main (string [] args) {hashset <Rectobject> set = new Hashset <rectobject> (); RectObject R1 = новый RectObject (3,3); RectObject R2 = новый RectObject (5,5); Rectobject r3 = new RectObject (3,3); set.add (r1); set.add (r2); set.add (r3); set.add (r1); System.out.println ("size:"+set.size ()); }} Мы сохранили четыре объекта в хэшсет и напечатали размер коллекции сет. Какой результат?
Результат работы: размер: 2
Почему это 2? Это очень просто, потому что мы переписываем метод хэшкода класса RectObject. Пока значения атрибута x и y re -rectobject равны, то его значения хэшкода также равны. Так что сначала сравните значения хэшкода. Значения атрибута X и Y объектов R1 и R2 не равны, поэтому их хэшкоды различны, поэтому объект R2 может быть введен, но значения атрибута x и y r3 такие же, как и значения атрибута объекта R1, поэтому хэшкод одинакова. В настоящее время мы сравниваем метод равных R1 и R3, потому что значения X и Y двух равны, поэтому объекты R1 и R3 равны, поэтому R3 нельзя поместить. Кроме того, добавление R1 в конце не добавлено, поэтому есть только два объекта, R1 и R2 в установленном наборе.
Далее мы комментируем метод хэшкода в объекте RectObject, то есть мы не переопределяем метод хэшкода в объекте объекта и запускаем код:
Результат работы: размер: 3
Этот результат также очень прост. Во -первых, оценить хешкод объекта R1 и объекта R2. Поскольку метод HashCode в объекте возвращает результат преобразования локального адреса памяти объекта, хэшкод разных объектов экземпляра отличается. Точно так же, поскольку хешкод R3 и R1 также неравен, но r1 == r1, поэтому в последнем наборе есть только три объекта R1, R2 и R3, поэтому размер равен 3
Далее мы комментируем содержимое метода Equals в объекте RectoBject, непосредственно возвращаем False, не комментируя метод хэшкода и запустить код:
Результат работы: размер: 3
Этот результат немного неожиданный, давайте проанализируем его:
Во -первых, объекты R1 и R2 сравнивают хэшкод, которые не равны, поэтому R2 помещается в установку, а затем посмотрите на методы HashCode R3 и сравните R1 и R3, которые равны, а затем сравнивают их методы равных. Поскольку метод equals всегда возвращает ложь, R1 и R3 также неравны, и нет необходимости упоминать R3 и R2. Хушкоды их двух не равны, поэтому R3 помещается в установку, а затем посмотрите на R4 и сравните R1 и R4. Найдите, что хэшкоды равны. При сравнении метода Equals, поскольку равные возвращают false, R1 и R4 не равны, те же R2 и R4 также неравны, а R3 и R4 также неравны, поэтому R4 можно помещать в набор, поэтому результат должен быть размер: 4, так почему это 3?
В настоящее время нам нужно проверить исходный код хэшса. Вот исходный код метода добавления в хэшсете:
/*** Добавляет указанный элемент в этот набор, если он еще не присутствует. * Более формально добавляет указанный элемент <TT> e </tt> к этому набору, если * этот набор не содержит элемента <TT> e2 </tt> такого, что * <tt> (e == null? E2 == null: e.equals (e2)) </tt>. * Если этот набор уже содержит элемент, вызов оставляет набор * без изменений и возвращает <tt> false </tt>. * * @param e Элемент, который будет добавлен в этот набор * @return <tt> true </ tt> Если этот набор еще не содержит указанный элемент */ public boolean add (e e) {return map.put (e, present) == null; } Здесь мы видим, что хэшсет фактически реализован на основе HashMap. Мы нажимаем на метод POT Hashmap, исходный код заключается в следующем:
/*** Сообщает указанное значение с указанным ключом на этой карте. * Если карта ранее содержала отображение для ключа, старое * значение заменяется. * * @param клавиша клавиши, с которой указанное значение должно быть связано * @param значение значения, связанное с указанной ключом * @return предыдущее значение, связанное с <tt> key </tt> или * <tt> null </tt>, если не было отображения для <tt> key </tt>. * (A <tt> null </tt> return также может указывать на то, что карта * ранее связана <tt> null </tt> с <tt> key </tt>.) */Public v (k key, value) {if (key = null) return putfornullkey (значение); int hash = hash (ключ); int i = indexfor (hash, table.length); for (intry <k, v> e = table [i]; e! = null; e = e.next) {объект k; if (e.hash == hash && ((k = e.key) == key || key.equals (k))) {v oldvalue = e.value; e.value = значение; e.recordaccess (это); вернуть OldValue; }} modcount ++; AddEntry (хэш, ключ, значение, i); вернуть ноль; } Давайте в основном посмотрим на условия суждения, если.
Во -первых, мы определяем, является ли хэшкод равным. Если это не равно, пропустите его напрямую. Если это равно, то сравните, являются ли эти два объекта равными или равенным методом этих двух объектов. Поскольку он выполняется или эксплуатируется, если он правда, мы можем объяснить это здесь. Фактически, размер вышеупомянутого набора составляет 3, потому что последний R1 не был введен, и считалось, что R1 == R1 вернул True, поэтому он не был включен. Таким образом, размер набора 3. Если мы установим метод хэшкода, чтобы всегда вернуть False, этот набор будет 4.
Наконец, давайте посмотрим на утечку памяти, вызванную HashCode: посмотрите на код:
пакет com.weijia.demo; импортировать java.util.hashset; открытый класс демонстрация {public static void main (string [] args) {hashset <Rectobject> set = new Hashset <rectobject> (); RectObject R1 = новый RectObject (3,3); RectObject R2 = новый RectObject (5,5); Rectobject r3 = new RectObject (3,3); set.add (r1); set.add (r2); set.add (r3); r3.y = 7; System.out.println ("размер перед удалением:"+set.size ()); set.remove (r3); System.out.println ("размер после удаления:"+set.size ()); }} Результаты работы:
Размер перед удалением: 3
Удаленный размер: 3
Раш, я нашел проблему, и это была большая проблема. Мы позвонили Remod, чтобы удалить объект R3, думая, что он был удален, но на самом деле он не был удален. Это называется утечка памяти, которая является неиспользованным объектом, но он все еще находится в памяти. Поэтому после того, как мы работаем так много раз, память взрывается. Посмотрите на исходный код удаления:
/*** Удаляет указанный элемент из этого набора, если он присутствует. * Более формально, удаляет элемент <TT> e </tt> такой, что * <tt> (o == null? E == null: o.equals (e)) </tt>, * Если этот набор содержит такой элемент. Возвращает <tt> true </tt> if * Этот набор содержал элемент (или эквивалентно, если этот набор * изменился в результате вызова). (Этот набор не будет содержать элемент *, как только вызов вернется.) * * @Param o объект, который будет удален из этого набора, если присутствует * @return <tt> true </ tt>, если набор содержал указанный элемент */ public boolean remove (Object o) {return map.remove (o) == присутствует; } Затем посмотрите на исходный код метода удаления:
/*** Удаляет отображение для указанного ключа с этой карты, если присутствует. * * @param Ключ, чье отображение должно быть удалено с карты * @return Предыдущее значение, связанное с <tt> key </tt> или * <tt> null </tt>, если не было отображения для <tt> key </tt>. * (A <tt> null </tt> return также может указывать на то, что карта * ранее связанная <tt> null </tt> с помощью <tt> key </tt>.) */Public v remove (jember key) {intry <k, v> elementEntryForkey (key); возврат (e == null? null: e.value); } Давайте посмотрим на исходный код метода remoxentryforkey:
/** * Удаляет и возвращает запись, связанную с указанным ключом * в HashMap. Возвращает NULL, если HashMap не содержит картирования * для этого ключа. */ Окончательная запись <K, v> removeEntryForkey (Object Key) {int hash = (key == null)? 0: хэш (ключ); int i = indexfor (hash, table.length); Вход <K, v> prev = table [i]; Запись <K, v> e = prev; while (e! = null) {inpit <k, v> next = e.next; Объект k; if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k))) {modcount ++; размер--; if (prev == e) таблица [i] = Далее; else prev.next = Далее; e.recordremoval (это); вернуть E; } prev = e; e = Далее; } return e; } Мы видим, что при вызове метода удаления мы сначала используем значение хэшкода объекта, чтобы найти объект, а затем удалить его. Эта проблема заключается в том, что мы изменили значение y -атрибута объекта R3. И поскольку метод HashCode объекта RectObject имеет значение Y, участвующее в операции, хэшкод объекта R3 изменился, поэтому R3 не обнаруживается в методе удаления, поэтому удаление не удалось. То есть хешкод R3 изменился, но местоположение, которое он хранит, не обновляется и все еще находится в своем исходном месте, поэтому, когда мы используем его новый хэшкод, чтобы найти его, мы определенно не найдем его.
Фактически, приведенный выше метод очень прост в реализации: как показано на рисунке:
Очень простая линейная хэш -таблица, используемая функция хэш - это мод, исходный код заключается в следующем:
/*** Возвращает индекс для хэш -кода h. */ static int indexfor (int h, int length) {return h & (длина-1); } Это на самом деле операция мода, но такая операция более эффективна, чем % работы.
1,2,3,4,5 означает результат MOD, и каждый элемент соответствует связанной структуре списка. Поэтому, если вы хотите удалить запись <K, v>, вы сначала получите хэшкод, чтобы получить узлом заголовка связанного списка, а затем выполнить через связанный список. Если хэшкод и равные равны, удалите этот элемент.
Приведенная выше утечка памяти рассказывает мне сообщение: если мы участвуем в работе хэшкода значения атрибута объекта, мы не можем изменить его значение атрибута при его удалении, в противном случае возникнут серьезные проблемы.
Фактически, мы также можем посмотреть на метод HashCode и равен методе типов объектов, соответствующих 8 основным типам данных.
Среди них хешкод базового типа в 8 очень прост в непосредственном возвращении их численного размера. Строковой объект выполняется с помощью сложного метода расчета, но этот метод расчета может гарантировать, что если значения этой строки равны, их хэшкод будет равным. 8 основных типов равенства методов предназначены непосредственно сравнивать числовые значения, а метод типа строки равняется значениям строк.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.