文档章节

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

tanzhgo077
 tanzhgo077
发布于 2014/09/16 20:16
字数 1307
阅读 125
收藏 14

首先说建议的情况:  比如你的对象想放到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简单的实现,我们来看看你就明白了: 

 

  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] == nullreturn 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 == nullcontinue;  

  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

tanzhgo077
粉丝 0
博文 8
码字总数 1334
作品 0
海淀
程序员
私信 提问
java重写equals()方法和hashCode()方法

1.equals()方法和hashCode()方法是什么? equals()和hashCode()都是是Java中万物之源Object类中的方法; equals方法用于比较两个对象是否相同,Object类中equals方法的实现是比较引用地址来判...

天王盖地虎626
06/19
51
0
hashcode()和equals()的是是非非

我们在很多博客的文章当中,我们都看到这样一句话:在重写equals方法的同时一定要重写hashCode方法。这是为什么?很多人会说,我的业务代码,只用equals比较比较两个对象是否相等不就可以了,...

似是而非Sage
2016/08/18
60
0
hashcode和equals

若两个对象equals相等(重写了equals方法),但不在一个区间(没有重写hashcode方法),因为hashCode的值在重写之前是对内存地址计算得出,所以根本没有机会进行比较,会被认为是不同的对象。所...

WJobs
2018/06/15
16
0
Java 中正确使用 hashCode 和 equals 方法

在这篇文章中,我将告诉大家我对hashCode和equals方法的理解。我将讨论他们的默认实现,以及如何正确的重写他们。我也将使用Apache Commons提供的工具包做一个实现。 目录: hashCode()和equ...

王振威
2012/10/21
67.2K
43
细说equals()方法和hashCode()方法

一、前言 对于这两个方法的研究,源于一道比较经典的面试题:“x.equals(y)==true;x,y可有不同的hashcode对吗?”,其实这道题的关键在于考我们对equals()方法和hashCode()方法的理解,网上看...

EnjoyAndroid
2017/11/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

浅谈prototype原型模式

一、原型模式简介 原型(Prototype)模式是一种对象创建型模式,他采取复制原型对象的方法来创建对象的实例。使用原型模式创建的实例,具有与原型一样的数据。 原型模式的特点: 1、由原型对...

青衣霓裳
20分钟前
6
0
shell mysql 备份

#!/bin/bash time2=$(date "+%Y-%m-%d-%H:%M:%S") /usr/local/mysql/bin/mysqldump -uroot -p ad > /usr/local/mysql/backup/"$time2".sql 变量引用原来是这么用的。......

奋斗的小牛
28分钟前
4
0
Jmeter监控Linux服务器操作

系统:Win7 64位 工具:Jmeter 4.0 要准备好的插件:JMeterPlugins-Standard-1.4.0,ServerAgent-2.2.1 解压JMeterPlugins-Standard-1.4.0.zip,将其中\lib\ext\JMeterPlugins-Standard.jar......

魔鬼妹子
28分钟前
5
0
系列文章:云原生Kubernetes日志落地方案

在Logging这块做了几年,最近1年来越来越多的同学来咨询如何为Kubernetes构建一个日志系统或者是来求助在这过程中遇到一系列问题如何解决,授人以鱼不如授人以渔,于是想把我们这些年积累的经...

Mr_zebra
29分钟前
5
0
入门必备!快速学会用Aspose.Words在表格中插入和删除列!

Aspose.Words For .Net(点击下载)是一种高级Word文档处理API,用于执行各种文档管理和操作任务。API支持生成,修改,转换,呈现和打印文档,而无需在跨平台应用程序中直接使用Microsoft W...

mnrssj
34分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部