在重写了对象的equals方法后,还需要重写hashCode方法吗?_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 在重写了对象的equals方法后,还需要重写hashCode方法吗?

在重写了对象的equals方法后,还需要重写hashCode方法吗?

 2014/5/4 21:40:59  myhadoop  程序员俱乐部  我要评论(0)
  • 摘要:首先说建议的情况:比如你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。然后再说说必须重写hashCode()的情况:如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet
  • 标签:方法 has Hash

首先说建议的情况:? 比如你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。?

然后再说说必须重写hashCode()的情况:?
??? 如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。?

这里我想讲讲sun的设计者为什么需要设计hashcode方法,也许这样你就应该知道什么时候该重写它了。?
数据结构有一种为了提高查找的效率而存在的数据结构——散列表,散列表其实是普通数组概念的推广,因为可以对数组进行直接寻址,故可以再O(1)时间内访问数组的任意元素,thinking in java中有个对hashmap简单的实现,我们来看看你就明白了:?

?

    class="dp-j">
  1. //:?containers/SimpleHashMap.java??
  2. //?A?demonstration?hashed?Map.??
  3. import?java.util.*;??
  4. import?net.mindview.util.*;??
  5. ??
  6. public?class?SimpleHashMap<K,V>?extends?AbstractMap<K,V>?{??
  7. ??//?Choose?a?prime?number?for?the?hash?table??
  8. ??//?size,?to?achieve?a?uniform?distribution:??
  9. ??static?final?int?SIZE?=?997;??
  10. ??//?You?can't?have?a?physical?array?of?generics,??
  11. ??//?but?you?can?upcast?to?one:??
  12. ??@SuppressWarnings("unchecked")??
  13. ??LinkedList<MapEntry<K,V>>[]?buckets?=??
  14. ????new?LinkedList[SIZE];//List数组里每项是个List,数组下标是hashcode方法的返回值再经过散列函数得到的,相当于散列表的关键字,它亦代表着每个对象的关键字。(不能显示new一个泛型数组,但是你可以new一个泛型数组的引用,如有需要以后可以将普通数组转型为泛型数组)。?
  15. ??public?V?put(K?key,?V?value)?{//将这个对键值放进hashmap中。
  16. ????V?oldValue?=?null;??
  17. ????int?index?=?Math.abs(key.hashCode())?%?SIZE;//这里就是通过散列函数对hashcode的返回值处理得到一个关键字,它代表了对象在数组里的位置,既是数组下标。
  18. ????if(buckets[index]?==?null)??
  19. ??????buckets[index]?=?new?LinkedList<MapEntry<K,V>>();//如果是第一次散列到这个数组下标,那么就新生成一个LinkedList,可以看到它里面保存的是MapEntry<K,V>键和值。?
  20. ????LinkedList<MapEntry<K,V>>?bucket?=?buckets[index];//将这个LinkedList赋值给一个bucket(桶),以后就直接在这个bucket进行操作。
  21. ????MapEntry<K,V>?pair?=?new?MapEntry<K,V>(key,?value);??
  22. ????boolean?found?=?false;??
  23. ????ListIterator<MapEntry<K,V>>?it?=?bucket.listIterator();??
  24. ????while(it.hasNext())?{??
  25. ??????MapEntry<K,V>?iPair?=?it.next();??
  26. ??????if(iPair.getKey().equals(key))?{//如果已经存在同一个键值,那么就更新value。?
  27. ????????oldValue?=?iPair.getValue();??
  28. ????????it.set(pair);?//?Replace?old?with?new??
  29. ????????found?=?true;??
  30. ????????break;??
  31. ??????}??
  32. ????}??
  33. ????if(!found)??
  34. ??????buckets[index].add(pair);//如果是一个新的键值,那么直接添加到这个LinkedList中。
  35. ????return?oldValue;??
  36. ??}??
  37. ??public?V?get(Object?key)?{//看hashmap是如何凭借hashcode方法快速定位到键值。?
  38. ????int?index?=?Math.abs(key.hashCode())?%?SIZE;[color=red]//与put方法中的作用一样,生成数组下标,因为我存的时候就是存到这个地方的,那么我取的时候直接访问buckets[index]。[/color]??
  39. ????if(buckets[index]?==?null)?return?null;//直接访问这个数组下标的LinkedList,如果为null,则返回。
  40. ????for(MapEntry<K,V>?iPair?:?buckets[index])//为什么要用LinkedList,因为hashcode方法产生的散列码不能完全确定一个对象,也就是说会和其他对象发生“碰撞”,即散列到同一个数组下标,解决这个问题的组号办法就是定义一个List把它们保存起来,但是在这个List中,我们必须保证能用equals方法确定对象的身份,这也就是为什么很多人说hashcode()相等,equals()不一定相等,而equals()相等的两个对象,hashcode()一定相等。所以这里要直接在LinkedList执行线性查找。
  41. ??????if(iPair.getKey().equals(key))??
  42. ????????return?iPair.getValue();??
  43. ????return?null;??
  44. ??}??
  45. ??public?Set<Map.Entry<K,V>>?entrySet()?{??
  46. ????Set<Map.Entry<K,V>>?set=?new?HashSet<Map.Entry<K,V>>();??
  47. ????for(LinkedList<MapEntry<K,V>>?bucket?:?buckets)?{??
  48. ??????if(bucket?==?null)?continue;??
  49. ??????for(MapEntry<K,V>?mpair?:?bucket)??
  50. ????????set.add(mpair);??
  51. ????}??
  52. ????return?set;??
  53. ??}??
  54. ??public?static?void?main(String[]?args)?{??
  55. ????SimpleHashMap<String,String>?m?=??
  56. ??????new?SimpleHashMap<String,String>();??
  57. ????m.putAll(Countries.capitals(25));??
  58. ????System.out.println(m);??
  59. ????System.out.println(m.get("ERITREA"));??
  60. ????System.out.println(m.entrySet());??
  61. ??}??
  62. }?/*?Output:?
  63. {CAMEROON=Yaounde,?CONGO=Brazzaville,?CHAD=N'djamena,?COTE?D'IVOIR?(IVORY?COAST)=Yamoussoukro,?CENTRAL?AFRICAN?REPUBLIC=Bangui,?GUINEA=Conakry,?BOTSWANA=Gaberone,?BISSAU=Bissau,?EGYPT=Cairo,?ANGOLA=Luanda,?BURKINA?FASO=Ouagadougou,?ERITREA=Asmara,?THE?GAMBIA=Banjul,?KENYA=Nairobi,?GABON=Libreville,?CAPE?VERDE=Praia,?ALGERIA=Algiers,?COMOROS=Moroni,?EQUATORIAL?GUINEA=Malabo,?BURUNDI=Bujumbura,?BENIN=Porto-Novo,?BULGARIA=Sofia,?GHANA=Accra,?DJIBOUTI=Dijibouti,?ETHIOPIA=Addis?Ababa}?
  64. Asmara?
  65. [CAMEROON=Yaounde,?CONGO=Brazzaville,?CHAD=N'djamena,?COTE?D'IVOIR?(IVORY?COAST)=Yamoussoukro,?CENTRAL?AFRICAN?REPUBLIC=Bangui,?GUINEA=Conakry,?BOTSWANA=Gaberone,?BISSAU=Bissau,?EGYPT=Cairo,?ANGOLA=Luanda,?BURKINA?FASO=Ouagadougou,?ERITREA=Asmara,?THE?GAMBIA=Banjul,?KENYA=Nairobi,?GABON=Libreville,?CAPE?VERDE=Praia,?ALGERIA=Algiers,?COMOROS=Moroni,?EQUATORIAL?GUINEA=Malabo,?BURUNDI=Bujumbura,?BENIN=Porto-Novo,?BULGARIA=Sofia,?GHANA=Accra,?DJIBOUTI=Dijibouti,?ETHIOPIA=Addis?Ababa]?

?

怎样?现在应该知道hashcode方法的作用了吧,它就是用来提高效率的,有句话说得好:为速度而散列。因为散列的Set和Map是基于hashcode方法来查找对象的,所以你在使用这些类的时候一定要覆盖hashcode方法,而非散列的Set和Map,例如TreeSet,TreeMap等,它们只需equals方法就可以唯一确定对象的身份。这样说,想必已经很清楚了吧。

转:?http://blog.csdn.net/huxin1/article/details/6325051

发表评论
用户名: 匿名