实例:
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Question)) {
return false;
}
Question question = (Question)o;
return question.question_id == this.question_id;
}
@Override
public int hashCode() {
return this.question_id;
}
经常会有这样的操作,hibernate拿到1对多的关系集合,进行删除操作.那么这个时候一般是HashSet,那么只需要重写hashCode方法。
代码示例:
try {
if (zhuanjia != null) {
Hibernate.initialize(user.getGzzhuanjia());
Set<Customer> gzZhuanjia = user.getGzzhuanjia();
gzZhuanjia.remove(zhuanjia); //删除Set集合中的对象,只需要重写hashcode方法。
user.setGzzhuanjia(gzZhuanjia);
customerDao.update(user);
return ServiceConfig.returnj("", null);
} else {
return ServiceConfig.returnj("未查找到需要取消关注的专家", null);
}
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();// 手动回滚
return ServiceConfig.returnj("系统错误", null);
}
今天突然碰到了这样的问题,代码如下:
Map<Question,List<QuestionAnswer>> map = new HashMap<Question, List<QuestionAnswer>>();
for (int i = 0; i < answerList.size(); i++) {
answer = answerList.get(i); // 获取当前回答对象
theQuestion = answer.getQuestion();// 获取回答对象所属问题
if (theQuestion != null) {
mapValue = map.get(theQuestion); // 通过指定key获取mapValue
// 如果当前key未查出value,说明map中不存在,那么添加一条key value,如果key查出了value,说明map已经存在,直接添加一条value的值。
if (mapValue == null || mapValue.size() == 0) {
mapValue = new ArrayList<QuestionAnswer>();
}
mapValue.add(answer);
map.put(theQuestion, mapValue);// 更新当前key的value
}
}
如题:
question对象为key,如何也获取不到,经过网上的一番查找,如下解释,才知道需要重写equals方法,说实话我没看懂,大致看了第一行和第二行。
大师讲解:
你的对象想放到Set集合或者是想作为Map的key时(非散列的Set和Map,例如TreeSet,TreeMap等),那么你必须重写equals()方法,这样才能保证唯一性。对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。
再说说必须重写hashCode()的情况:
如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。
这里我想给楼主讲讲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")
- LinkedList<MapEntry<K,V>>[] buckets =
- new LinkedList[SIZE];[color=red]//List数组里每项是个List,数组下标是hashcode方法的返回值再经过散列函数得到的,相当于散列表的关键字,它亦代表着每个对象的关键字。(不能显示new一个泛型数组,但是你可以new一个泛型数组的引用,如有需要以后可以将普通数组转型为泛型数组)。[/color]
- public V put(K key, V value) {[color=red]//将这个对键值放进hashmap中。[/color]
- V oldValue = null;
- int index = Math.abs(key.hashCode()) % SIZE;[color=red]//这里就是通过散列函数对hashcode的返回值处理得到一个关键字,它代表了对象在数组里的位置,既是数组下标。[/color]
- if(buckets[index] == null)
- buckets[index] = new LinkedList<MapEntry<K,V>>();[color=red]//如果是第一次散列到这个数组下标,那么就新生成一个LinkedList,可以看到它里面保存的是MapEntry<K,V>键和值。[/color]
- LinkedList<MapEntry<K,V>> bucket = buckets[index];[color=red]//将这个LinkedList赋值给一个bucket(桶),以后就直接在这个bucket进行操作。[/color]
- 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)) {[color=red]//如果已经存在同一个键值,那么就更新value。[/color]
- oldValue = iPair.getValue();
- it.set(pair); // Replace old with new
- found = true;
- break;
- }
- }
- if(!found)
- buckets[index].add(pair);[color=red]//如果是一个新的键值,那么直接添加到这个LinkedList中。[/color]
- return oldValue;
- }
- public V get(Object key) {[color=red]//看hashmap是如何凭借hashcode方法快速定位到键值。[/color]
- int index = Math.abs(key.hashCode()) % SIZE;[color=red]//与put方法中的作用一样,生成数组下标,因为我存的时候就是存到这个地方的,那么我取的时候直接访问buckets[index]。[/color]
- if(buckets[index] == null) return null;[color=red]//直接访问这个数组下标的LinkedList,如果为null,则返回。[/color]
- for(MapEntry<K,V> iPair : buckets[index])[color=red]//为什么要用LinkedList,因为hashcode方法产生的散列码不能完全确定一个对象,也就是说会和其他对象发生“碰撞”,即散列到同一个数组下标,解决这个问题的组号办法就是定义一个List把它们保存起来,但是在这个List中,我们必须保证能用equals方法确定对象的身份,这也就是为什么很多人说hashcode()相等,equals()不一定相等,而equals()相等的两个对象,hashcode()一定相等。所以这里要直接在LinkedList执行线性查找。[/color]
- 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());
- }
- } /* Output:
- {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}
- Asmara
- [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方法就可以唯一确定对象的身份。这样说,想必已经很清楚了吧。