Я полагаю, что большинство людей знакомы со структурой данных хэш -таблицы, и во многих местах хэш -таблицы используются для повышения эффективности поиска. В классе объектных Java есть метод:
общественный родной int hashcode ();
Согласно объявлению этого метода, можно видеть, что метод возвращает числовое значение типа int и является локальным методом, поэтому в классе объекта не указана конкретная реализация.
Зачем классу объекта нужен такой метод? Какова его функция? Сегодня мы подробно обсудим метод хэшкода.
1. Функция метода хэшкода
Для языков программирования, которые содержат типы контейнеров, в основном участвует хэшкод. То же самое верно на Java. Основная функция метода хэшкода заключается в том, чтобы нормально работать с наборами на основе хеш, такие хэш-наборы включают хэшсет, хэшмап и хештибель.
Почему ты так говоришь? Рассмотрим ситуацию, когда, когда объект вставлен в коллекцию, как определить, существует ли объект в коллекции? (Примечание: дублирующие элементы не допускаются в коллекции)
Возможно, большинство людей подумают о том, чтобы призвать метод Equals для сравнения одного за другим, и этот метод действительно возможен. Однако, если в наборе уже есть десять тысяч данных или более данных, если метод Equals используется для сравнения одного за другим, эффективность неизбежно будет проблемой. В настоящее время отражается функция метода HashCode. Когда новый объект должен быть добавлен в коллекцию, метод хэшкода объекта называется первым, чтобы получить соответствующее значение хэшкода. Фактически, в конкретной реализации HashMap будет использоваться таблица для сохранения значения хэшкода объекта, который был сохранен. Если в таблице нет значения хэшкода, его можно хранить непосредственно без какого -либо сравнения. Если значение HashCode существует, его метод равных вызывается для сравнения с новым элементом. Если то же самое верно, другие адреса не будут храниться. Следовательно, здесь существует проблема разрешения конфликтов. Таким образом, количество раз, когда метод равных фактически называется значительно уменьшен. Проще говоря: метод хэшкода в Java отображает информацию, связанную с объектом (например, адрес хранения объекта, поле объекта и т. Д.) В числовое значение в соответствии с определенными правилами, и это значение называется hash value. Следующий код является конкретной реализацией метода POT в java.util.hashmap:
public v put (k key, v value) {if (key == null) return putfornullkey (value); int hash = hash (key.hashcode ()); 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); вернуть ноль; } Метод POT используется для добавления нового элемента в HashMap. Из конкретной реализации метода POT мы можем знать, что метод хэшкода будет сначала вызван для получения значения хэшкода элемента, а затем проверить, существует ли значение хэшкода в таблице. Если он существует, вызовите метод Equals, чтобы пересмотреть, существует ли элемент. Если оно существует, обновите значение значения, в противном случае добавьте новый элемент в HashMap. Отсюда мы видим, что существует метод хэшкода, чтобы уменьшить количество вызовов к методу equals и, таким образом, повысить эффективность программы.
Некоторые друзья по ошибке думают, что по умолчанию HashCode возвращает адрес хранения объекта. На самом деле, эта точка зрения неполна. Это правда, что некоторые JVM напрямую возвращают адрес хранения объекта при реализации, но это не так большую часть времени. Можно только сказать, что адрес хранения может быть связан с ним. Ниже приводится реализация генерирующих значений хеш -хэш в горячей точке JVM:
статический inline intptr_t get_next_hash (think * self, oop obj) {intptr_t value = 0; if (hashcode == 0) {// В этой форме используется неохраняемый глобальный RNG Park-Miller, // так что две потоки могут участвовать в гонке и генерировать один и тот же RNG. // В системе MP у нас будет большой доступ RW к глобальному, поэтому механизм // вызывает много когерентного трафика. value = os :: random (); } else if (hashcode == 1) {// Это вариация имеет свойство стабильного (idempotent) // между операциями STW. Это может быть полезно в некоторых из схем синхронизации 1-0 //. intptr_t addrbits = intptr_t (obj) >> 3; value = addrbits ^ (addrbits >> 5) ^ gvars.stwrandom; } else if (hashcode == 2) {value = 1; // для тестирования чувствительности} else if (hashcode == 3) {value = ++ gvars.hc sequestence; } else if (hashcode == 4) {value = intptr_t (obj); } else {// схема XOR-сдвига Marsaglia с конкретным состоянием потока // Это, вероятно, лучшая общая реализация-мы //, вероятно, сделаем это по умолчанию в будущих выпусках. unsigned t = self-> _ hashstatex; t ^= (t << 11); Self-> _ hashstatex = self-> _ hashstatey; Self-> _ hashstatey = self-> _ hashstatez; Self-> _ hashstatez = self-> _ hashstatew; unsigned v = self-> _ hashstatew; v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)); Self-> _ hashstatew = v; значение = V; } value & = markoopdesc :: hash_mask; if (value == 0) value = 0xbad; assert (value! = markoopdesc :: no_hash, "инвариант"); Tevent (Hashcode: Generate); возвращаемое значение;}Эта реализация находится в файле Hotspot/SRC/Share/VM/Runtime/Synchronizer.cpp.
Следовательно, некоторые люди могут сказать, что можем ли мы напрямую судить, равны ли два объекта на основе значения HashCode? Это, конечно, невозможно, потому что разные объекты могут генерировать одинаковое значение хэшкода. Хотя невозможно судить, равны ли два объекта на основе значения хэшкода, вы можете напрямую судить, что эти два объекта не равны на основе значения хэшкода. Если значения хэшкода двух объектов не равны, они должны быть двумя разными объектами. Если вы хотите определить, действительно ли два объекта равны, вы должны использовать метод Equals.
То есть для двух объектов, если результат, полученный при вызове метода equals, является истинной, значения хэшкода двух объектов должны быть равными;
Если результат, полученный методом Equals, является false, значения хэшкода двух объектов не могут быть разными;
Если значения хэшкода двух объектов не равны, результат, полученный методом Equals, должен быть ложным;
Если значения хэшкода двух объектов равны, результат, полученный методом Equals, неизвестен.
2. Метод equals и метод хэшкода
В некоторых случаях при разработке класса программисты должны переписать метод Equals, такой как класс строки, но обязательно отметьте, что, переписывая метод Equals, они должны переписать метод хэшкода. Почему ты так говоришь?
Давайте посмотрим пример ниже:
пакет com.cxh.test1; import java.util.hashmap; import java.util.hashset; import java.util.set; class People {private String name; частный int возраст; Public People (String name, int age) {this.name = name; this.age = возраст; } public void setage (int age) {this.age = age; } @Override public boolean equals (Object obj) {// todo автоматически генерируемый метод recute this.name.equals (((люди). }} открытый класс main {public static void main (string [] args) {people p1 = new People ("jack", 12); System.out.println (p1.hashcode ()); Hashmap <People, Integer> HashMap = новый HashMap <People, Integer> (); hashmap.put (p1, 1); System.out.println (hashmap.get (new People ("jack", 12));}}Здесь я только переписываю метод Equals, что означает, что если два объекта имеют одинаковое имя и возраст, они считаются одним и тем же человеком.
Первоначальное намерение этого кода состоит в том, чтобы вывести результат «1», но на самом деле оно выводит «NULL». Почему? Причина в том, что вы забыли переписать метод хэшкода, переписывая метод равных.
Хотя два объекта с одинаковым именем и возрастом логически определены как равные (аналогично классу строки), следует известно, что по умолчанию метод хэшкода отображает адрес хранения объекта. Тогда неудивительно, что выходной результат вышеуказанного кода «нулевой». Причина очень проста, на объект, на который указал P1 и
System.out.println (hashmap.get (Новые люди («Джек», 12)); новые люди («Джек», 12) в этом предложении генерирует два объекта, и их адреса хранения должны быть разными. Ниже приводится конкретная реализация метода получения хэшмапа:
public v get (object key) {if (key == null) return getFornullKey (); int hash = hash (key.hashcode ()); for (inpit <k, v> e = table [indexfor (hash, table.length)]; e! = null; e = e.next) {объект k; if (e.hash == hash && ((k = e.key) == key || key.equals (k))) return e.value; } return null; }Следовательно, когда HashMap используется для получения, поскольку полученные значения Hashcdoe различны (обратите внимание, что вышеупомянутый код может получить одинаковое значение хэшкода в некоторых случаях, но вероятность относительно невелика, потому что, хотя адреса хранения двух объектов различны, можно получить одинаковое значение HashCode), поэтому для LOOP не будет выполняться в методе GET.
Поэтому, если вы хотите, чтобы вышеупомянутый код выводил результат «1», он очень просто. Вам просто нужно переписать метод хэшкода и сделать метод equals и метод хэшкода всегда логически согласованным.
пакет com.cxh.test1; import java.util.hashmap; импорт java.util.hashset; импорт java.util.set; класс Pupe {Private String name; частный int возраст; Public People (String name, int age) {this.name = name; this.age = возраст; } public void setage (int age) {this.age = age; } @Override public int hashcode () {// todo автоматически генерируемый метод заглушка return name.hashcode ()*37+age; } @Override public boolean equals (Object obj) {// todo автоматически генерируемый метод recute this.name.equals (((люди). }} открытый класс main {public static void main (string [] args) {people p1 = new People ("jack", 12); System.out.println (p1.hashcode ()); Hashmap <People, Integer> HashMap = новый HashMap <People, Integer> (); hashmap.put (p1, 1); System.out.println (hashmap.get (новые люди ("Джек", 12))); }}Таким образом, результатом выходного вывода будет «1».
Следующий отрывок выдержит из книги эффективной Java:
Во время выполнения программы, до тех пор, пока информация, используемая в операции сравнения метода Equals, не была изменена, метод HashCode должен последовательно возвращать одно и то же целое число.
Если два объекта равны в соответствии с методом Equals, то вызов метода HashCode двух объектов должен вернуть один и тот же целый ряд.
Если эти два объекта сравнивают неравенство в соответствии с методом Equals, метод хэшкода не обязательно возвращает разные целые числа.
Легко понять вторые и третьи статьи, но первая статья часто игнорируется. Существует также отрывок, похожий на первый на странице P495 в книге «Мысль программирования Java»:
«Наиболее важным фактором при разработке hashcode () является: всякий раз, когда вы называете hashcode () на одном и том же объекте, должно генерироваться одно и то же значение. Если объект добавляется в Hashmap с помощью put (), а другое значение хэшкода генерируется, когда он извлекается с помощью get (), тогда объект не может быть получен. Поэтому, если ваш метод HashCode зависит от вариабельных данных в объекте, пользователи должны быть в целом. Метод hashcode () будет генерировать другой хэш -код. "
Вот пример:
пакет com.cxh.test1; Импорт java.util.hashmap; import java.util.hashset; import java.util.set; класс People {Private String name; частный int возраст; Public People (String name, int age) {this.name = name; this.age = возраст; } public void setage (int age) {this.age = age; } @Override public int hashcode () {// todo автоматически генерируемый метод заглушка return name.hashcode ()*37+age; } @Override public boolean equals (Object obj) {// todo автоматически генерируемый метод recute this.name.equals (((люди). }} открытый класс main {public static void main (string [] args) {people p1 = new People ("jack", 12); System.out.println (p1.hashcode ()); Hashmap <People, Integer> HashMap = новый HashMap <People, Integer> (); hashmap.put (p1, 1); P1.Setage (13); System.out.println (hashmap.get (p1)); }}Результатом этого вывода кода является «NULL», и каждый должен четко понимать причины.
Следовательно, при разработке методов хэшкодов и равных методов, если данные в объекте летучи, лучше не полагаться на это поле в методах равных и методах хэшкодов.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.