2014-10-10 5 views
2

Возможно, это не сценарий реального мира, а просто любопытно узнать, что происходит, ниже - код.Что произойдет, если мы переопределим только hashCode() в классе и используем его в Set?

Я создаю набор объектов класса UsingSet. Согласно идее хэширования в Java, когда я впервые добавляю объект, который содержит «a», он создаст ведро с hashcode 97 и поместит в него объект. Опять же, когда он встречает объект с «a», он вызовет переопределенный метод hashcode в классе UsingSet, и он получит hashcode 97, и что дальше?

Как я не переопределил метод equals, реализация по умолчанию вернет false. Итак, где будет храниться объект со значением «a», в том же ведре, где хранился предыдущий объект с hashcode 97? или он создаст новое ведро? Кто-нибудь знает, как он будет храниться внутри?

/* package whatever; // don't place package name! */ 

import java.util.*; 
import java.lang.*; 
import java.io.*; 

class UsingSet { 

    String value; 

    public UsingSet(String value){ 
    this.value = value; 
    } 

    public String toString() { 
    return value; 
    } 

    public int hashCode() { 
    int hash = value.hashCode(); 
    System.out.println("hashcode called" + hash); 
    return hash; 
    } 

    public static void main(String args[]) { 

    java.util.Set s = new java.util.HashSet(); 

    s.add(new UsingSet("A")); 
    s.add(new UsingSet("b")); 
    s.add(new UsingSet("a")); 
    s.add(new UsingSet("b")); 
    s.add(new UsingSet("a")); 

    s.add(new Integer(1)); 
    s.add(new Integer(1)); 

    System.out.println("s = " + s); 

    } 
} 

выход:

hashcode called65 
hashcode called98 
hashcode called97 
hashcode called98 
hashcode called97 
s = [1, b, b, A, a, a] 
+0

Внутренне «HashMap» используется для хранения значений «HashSet». Кроме того, существует метод 'hash()' под 'HashMap', который применяет дополнительную хеш-функцию для защиты от слабых хэш-кодов. – TheLostMind

+0

Должно быть такое же ведро, вы можете проверить отладчик. –

ответ

4

Джеймс Большой ответ неверен, или, скорее, вводит в заблуждение (и частично неправильный). Я объясню.

Если два объекта равны в соответствии с их методом equals(), они также должны иметь одинаковый хэш-код. Если два объекта имеют один и тот же хэш-код, они НЕ должны быть равны.

Вот фактическая формулировка из документации java.util.Object:

  • Если два объекта равен по равным методы (Object), то вызов метода Hashcode на каждом из двух объекты должны давать одинаковый целочисленный результат.
  • Не требуется, чтобы, если два объекта неравны в соответствии с методом equals (java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен производить различные целочисленные результаты. Тем не менее, программист должен знать, что получение отдельных целых результатов для неравных объектов может улучшить производительность хеш-таблиц.

Это правда, что если два объекта не имеют один и тот же хэш, то они не равны. Однако хеширование - это не способ проверить равенство - так что дико неверно говорить, что это более быстрый способ проверить равенство.

Кроме того, также дико неверно говорить, что функция hashCode является эффективным способом сделать что-либо. Все это связано с реализацией, но реализация по умолчанию для hashCode строки очень неэффективна, поскольку String становится большой. Он будет выполнять вычисления на основе каждого символа строки, поэтому, если вы используете большие строки как ключи, это становится очень неэффективным; moreso, если у вас большое количество ведер.

На карте (HashSet использует HashMap внутренне), есть ведра и в каждом ведре есть связанный список. Java использует функцию hashCode(), чтобы выяснить, в каком ведре она входит (она фактически изменит хеш, в зависимости от того, сколько ведер существует). Поскольку два объекта могут совместно использовать один и тот же хэш, он будет последовательно перебирать связанный список последовательно, проверяя метод equals(), чтобы увидеть, является ли объект дубликатом. На java.util.Set documenation:

Коллекция, которая не содержит повторяющихся элементов.

Таким образом, если его хэш-код() приводит его в ковш, в котором это ведро содержит объект, где .equals() истинно, то предыдущий объект перезаписывается с новым объектом. Вы, вероятно, можете посмотреть здесь для получения дополнительной информации: How does a Java HashMap handle different objects with the same hash code?

Вообще говоря, хотя, это хорошая практика, что если вы перезаписать функцию Hashcode, вы также перезаписать функцию Equals (если я не ошибаюсь, это нарушает договора, если вы не хотите).

+0

Вы упомянули, что «если его hashCode() ведет его к ведру, в котором это ведро содержит объект, где .equals() оценивается как true, тогда предыдущий объект перезаписывается новый объект ". Но я хочу знать, что произойдет, если его hashCode() приведет его к ведру, в котором это ведро содержит объект, где .equals() оценивает (false) ". Я хочу знать, как два объекта будут храниться в одном и том же ковше внутри? – kumar

+1

Ну, как я уже сказал, каждое ведро - это связанный список. Хэш-код усечен относительно количества ковшей, чтобы сделать его более эффективным в сочетании с количеством сохраненных элементов. Это означает, что чем меньше количество сохраненных элементов, тем больше вероятность столкновения. Когда карта найдет ведро, принадлежащее хешу, он будет перебирать каждый элемент в связанном списке. Если он попадает в конец связанного списка и нет соответствующих элементов, он будет помещать новый элемент в конец. Если карта достигает своего коэффициента нагрузки, то она реорганизует всю карту и увеличивает количество ковшей. – searchengine27

+0

. Это немного не по теме, но коэффициент загрузки по умолчанию составляет 75%. Кроме того, вне темы снова, вот почему вы хотите установить начальную емкость, которая отражается в количестве всех элементов, которые, по вашему мнению, будут на карте, потому что для реорганизации карты становится все более дорогостоящим, поскольку количество сохраненных элементов увеличивается и больше. Вы хотите установить начальную емкость чуть больше максимального количества элементов, которые, по вашему мнению, будут сохранены, чтобы впоследствии уменьшить эту стоимость. – searchengine27

0

Не смотря на свой код ...

Весь смысл хэш-кодов, чтобы ускорить процесс проверки двух объектов на равенство. Это может быть дорогостоящим, чтобы проверить, равны ли два больших, сложных объекта, но легко сравнить их хэш-коды, а хэш-коды можно предварительно вычислить.

Правило: если два объекта не имеют одинакового хеш-кода, то означает они не равны. Не нужно делать дорогой тест на равенство.

Таким образом, ответ на вопрос в заголовке: Если вы определяете равенства() метод, который говорит, что объект А равно объект B, и определить метод хэш-код(), который говорит, объект А не равна для объекта B (т. е. он говорит, что у них разные хеш-коды), а затем вы передаете эти два объекта в какую-либо библиотеку, которая заботится о том, равны они или нет (например, если вы помещаете их в хеш-таблицу), то поведение библиотека будет неопределенной (т. е., вероятно, ошибочно).


Дополнительная информация: Wow! Я действительно пропустил увидеть лес для деревьев здесь, думая о цели hashCode(), не помещая его в контексте HashMap. Если m - это карта с N элементами, а k - ключ; Какова цель вызова m.get(k)? Целью, очевидно, является поиск карты для записи, ключ которой равен , равному по k.

Что делать, если хэш-коды и хэш-карты не были изобретены? Хорошо, что вы могли бы сделать, полагая, что ключи имеют естественный общий порядок, - это поиск TreeMap, сравнивая данный ключ для равенства с O (log (N)) другими ключами. В худшем случае, когда ключи не имеют порядка, вам нужно сравнить данный ключ для равенства с каждым ключом на карте, пока не найдете совпадение или не проверите их все. Другими словами, сложность m.get(k) будет O (N).

Когда m является HashMap, сложностью m.get(k) является O (1), можно ли заказать ключи или нет.

Итак, я испортил, сказав, что точка хеш-кодов должна ускорить процесс тестирования двух объектов для равенства.Это действительно касается тестирования объекта на равенство с целым набором других объектов. Вот где сравнение хеш-кодов не просто помогает немного; Это помогает на порядки ...

... Если в k.hashCode() и k.equals(o) методы подчиняются правилу: j.hashCode()!=k.hashCode() подразумевает !j.equals(k).

+0

Я рекомендую пересмотреть свой ответ или удалить его. Большинство из того, что вы сказали, неправильно, если не все, что вы сказали. EDIT: да, все, что вы сказали, неверно. – searchengine27

+0

@ searchengine27, ОК, поэтому вы говорите, что никогда не бывает дорого сравнивать большие сложные объекты для равенства, что всегда сложно сравнивать хеш-коды и что никогда не возможно/полезно, чтобы объект кэшировал свой хэш-код после вычислений это один раз. Вы говорите, что это нормально для двух объектов, которые имеют разные хеш-коды для проверки как равные, и что если вы помещаете подобные объекты в HashTable, HashTable будет делать что-то предсказуемое и полезное с ними. Или, наверное, может быть, «все, что вы сказали не так», может иметь какое-то новое значение, о котором я еще не слышал. –

+0

прочитал мой ответ, который я разместил. Я хотел обратиться к * некоторым * вещам, которые вы упомянули в моем ответе, но я непреднамеренно обратился ко всему, что неправильно в вашем ответе в моем ответе. Суммирование: вам нужно прочитать документацию. Что касается сравнения равенства строки, он будет сравнивать каждый символ в строке, которая является одной операцией для каждого символа. Хэш-код будет выполнять «h = 31 * h + val [off ++];» (5 операций) на каждом символе. Это означает, что hashCode ВСЕГДА в 5 раз дороже, чем равные. В любом случае это не имеет значения, поскольку в документации не говорится, что равные хэш-коды означают равенство. – searchengine27

0

Комплект будет вести себя по-другому.

Уникальность не будет. Потому что уникальное будет достигнуто с помощью методов hashcode и equals.
Выход будет понравиться s = [A, a, b, 1] вместо раннего.

Апартамент, который удаляет и содержит всю обыкновенную работу.

3

HashCode & Равно методы

  1. переопределять только Hashcode, используйте по умолчанию Равно: только ссылки на тот же объект возвращает истину. Другими словами, те объекты, которые вы ожидали быть равными, не будут равны, вызывая метод equals.
  2. Only Override Equals, Использовать HashCode по умолчанию: В HashMap или HashSet могут быть дубликаты. Мы пишем метод equals и ожидаем, что {"abc", "ABC"} будет равен. Однако при использовании HashMap они могут отображаться в разных кодах, поэтому метод не будет обнаруживать их друг друга.
0

Просто можно предположить, хэш и равняется методы, как поиск 2D, как: -

Где Hashcode это Ряды и список объектов является колонки. Рассмотрим следующую структуру классов.

public class obj 
    { 
    int Id; 
    String name; 
    public obj(String name,int id) 
    { 
     this.id=id; 
     this.name=name; 
    } 
    } 

теперь, если вы создаете объекты, как это: -

obj obj1=new obj("Hassu",1); 
obj obj2=new obj("Hoor",2); 
obj obj3=new obj("Heniel",3); 
obj obj4=new obj("Hameed",4); 
obj obj5=new obj("Hassu",1); 

и вы поместите это объекты на карте, как это: -

HashMap hMap=new HashMap(); 
    1. hMap.put(obj1,"value1"); 
    2. hMap.put(obj2,"value2"); 
    3. hMap.put(obj3,"value3"); 
    4. hMap.put(obj4,"value4"); 
    5. hMap.put(obj5,"value5"); 

теперь, если вы не переопределить хэш-код и равно тогда, после помещения всех объектов до строки 5, если вы поместите obj5 на карту, так как по умолчанию HashCode вы получаете другой hashCode, поэтому строка (Bucket будет отличаться). Таким образом, во время работы память будет храниться следующим образом.

|hashcode | Objects 
|-----------| --------- 
|000562  | obj1 
|000552  | obj2 
|000588  | obj3 
|000546  | obj4 
|000501  | obj5 

Теперь, если вы создаете тот же объект, как: - OBJ obj6 = новый OBJ ("hassu", 1); И если вы будете искать это значение в map.like

if(hMap.conaints(obj6)) 
or 
hMpa.get(obj 6); 

хотя ключ (obj1) с тем же содержанием доступен, вы получите ложные и нуль соответственно. Теперь, если вы переопределяете только метод equals. и выполнить тот же ключ поиска контента, также получит Null, поскольку HashCode для obj6 отличается и в этом хэш-коде вы не найдете ни одного ключа. Теперь, если вы переопределяете только метод hashCode.

Вы получите то же самое ведро (строка HashCode), но контент не может быть проверен, и он будет выполнять стандартную проверку объекта Super Object Class. Итак, если вы ищете ключ hMap.get (obj6), вы получите правильный хэш-код: - 000562, но поскольку ссылка для обоих obj1 и obj6 разная, вы получите нуль.