2.为什么在写了equals方法之后还要写hashcode方法
2016-04-17 22:35
417 查看
原本想自己写,但是看了这篇博客之后发现我还是太嫩了。
1.首先说建议的情况: 比如你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
2.然后再说说必须重写hashCode()的情况:
如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。
3.这里我想讲讲sun的设计者为什么需要设计hashcode方法,也许这样你就应该知道什么时候该重写它了。
数据结构有一种为了提高查找的效率而存在的数据结构——散列表,散列表其实是普通数组概念的推广,因为可以对数组进行直接寻址,故可以再O(1)时间内访问数组的任意元素,thinking in java中有个对hashmap简单的实现,我们来看看你就明白了:
现在应该知道hashcode方法的作用了吧,它就是用来提高效率的,有句话说得好:为速度而散列。因为散列的Set和Map是基于hashcode方法来查
找对象的,所以你在使用这些类的时候一定要覆盖hashcode方法,而非散列的Set和Map,例如TreeSet,TreeMap等,它们只需equals方法就可以唯一
确定对象的身份。这样说,想必已经很清楚了吧。
1.首先说建议的情况: 比如你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。当然,在这种情况下,你不想重写hashCode()方法,也没有错。但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
2.然后再说说必须重写hashCode()的情况:
如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。
3.这里我想讲讲sun的设计者为什么需要设计hashcode方法,也许这样你就应该知道什么时候该重写它了。
数据结构有一种为了提高查找的效率而存在的数据结构——散列表,散列表其实是普通数组概念的推广,因为可以对数组进行直接寻址,故可以再O(1)时间内访问数组的任意元素,thinking in java中有个对hashmap简单的实现,我们来看看你就明白了:
//: containers/SimpleHashMap.java // A demonstration hashed Map. import java.util.*; import net.mindview.util.*; public class SimpleHashMap<K,V> extends AbstractMap<K,V> { // Choose a prime number for the hash table // size, to achieve a uniform distribution: static final int SIZE = 997; // You can't have a physical array of generics, // but you can upcast to one: @SuppressWarnings("unchecked") /*List数组里每项是个List,数组下标是hashcode方法的返回值再经过散列函数得到的,相当于散列表的关键字,它亦代表着每个对象 的关键字。(不能显示new一个泛型数组,但是你可以new一个泛型数组的引用,如有需要以后可以将普通数组转型为泛型数组)。*/ LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE]; //将这个对键值放进hashmap中。 public V put(K key, V value) { V oldValue = null; //这里就是通过散列函数对hashcode的返回值处理得到一个关键字,它代表了对象在数组里的位置,既是数组下标。 int index = Math.abs(key.hashCode()) % SIZE; if(buckets[index] == null) //如果是第一次散列到这个数组下标,那么就新生成一个LinkedList,可以看到它里面保存的是MapEntry<K,V>键和值。 buckets[index] = new LinkedList<MapEntry<K,V>>(); //将这个LinkedList赋值给一个bucket(桶),以后就直接在这个bucket进行操作。 LinkedList<MapEntry<K,V>> bucket = buckets[index]; MapEntry<K,V> pair = new MapEntry<K,V>(key, value); boolean found = false; ListIterator<MapEntry<K,V>> it = bucket.listIterator(); while(it.hasNext()) { MapEntry<K,V> iPair = it.next(); if(iPair.getKey().equals(key)) { //如果已经存在同一个键值,那么就更新value。 oldValue = iPair.getValue(); it.set(pair); // Replace old with new found = true; break; } } if(!found) //如果是一个新的键值,那么直接添加到这个LinkedList中。 buckets[index].add(pair); return oldValue; } //看hashmap是如何凭借hashcode方法快速定位到键值。 public V get(Object key) { //与put方法中的作用一样,生成数组下标,因为我存的时候就是存到这个地方的,那么我取的时候直接访问buckets[index] int index = Math.abs(key.hashCode()) % SIZE; //直接访问这个数组下标的LinkedList,如果为null,则返回。 if(buckets[index] == null) return null; /*为什么要用LinkedList,因为hashcode方法产生的散列码不能完全确定一个对象,也就是说会 和其他对象发生“碰撞”,即散列到同一个数组下标,解决这个问题的组号办法就是定义一个List 把它们保存起来,但是在这个List中,我们必须保证能用equals方法确定对象的身份,这也就是为 什么很多人说hashcode()相等,equals()不一定相等,而equals()相等的两个对象,hashcode()一定 相等。所以这里要直接在LinkedList执行线性查找。*/ for(MapEntry<K,V> iPair : buckets[index]){ if(iPair.getKey().equals(key)) return iPair.getValue(); } return null; } public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> set= new HashSet<Map.Entry<K,V>>(); for(LinkedList<MapEntry<K,V>> bucket : buckets) { if(bucket == null) continue; for(MapEntry<K,V> mpair : bucket) set.add(mpair); } return set; } public static void main(String[] args) { SimpleHashMap<String,String> m = new SimpleHashMap<String,String>(); m.putAll(Countries.capitals(25)); System.out.println(m); System.out.println(m.get("ERITREA")); System.out.println(m.entrySet()); } }
现在应该知道hashcode方法的作用了吧,它就是用来提高效率的,有句话说得好:为速度而散列。因为散列的Set和Map是基于hashcode方法来查
找对象的,所以你在使用这些类的时候一定要覆盖hashcode方法,而非散列的Set和Map,例如TreeSet,TreeMap等,它们只需equals方法就可以唯一
确定对象的身份。这样说,想必已经很清楚了吧。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树