hashcode()方法和equals()方法。使用这两个方法,一个对象能够存储或从一个Hashtable,HashMap,HashSet 被检索。
hashcode():
public class Country { String name; long population; public String getName() { return name; } public void setName(String name) { this.name = name; } public long getPopulation() { return population; } public void setPopulation(long population) { this.population = population; } }
重写equals()方法 1:对象是否和当前对象为同一个对象;2.对象是否为空;3.对象所属类和当前对象所属类是否一样;4.具体属性的比较 注意:方法签名的书写
public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Country other = (Country) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
put this Country objects in hashmap
package org.arpit.java2blog; import java.util.HashMap; import java.util.Iterator; public class HashMapEqualityCheckMain { /** * @author Arpit Mandliya */ public static void main(String[] args) { HashMap<Country,String> countryCapitalMap=new HashMap<Country,String>(); Country india1=new Country(); india1.setName("India"); Country india2=new Country(); india2.setName("India"); countryCapitalMap.put(india1, "Delhi"); countryCapitalMap.put(india2, "Delhi"); Iterator<Country> countryCapitalIter=countryCapitalMap.keySet().iterator(); while(countryCapitalIter.hasNext()) { Country countryObj=countryCapitalIter.next(); String capital=countryCapitalMap.get(countryObj); System.out.println("Capital of "+ countryObj.getName()+"----"+capital); } } }
结果:
Capital of India----Delhi
Capital of India----Delhi
HashMap uses hashcode to find bucket for that key object, if hashcodes are same then only it checks for equals method and because hashcode for above two country objects uses default hashcode method,Both will have different memory address hence different hashcode.
override hashcode method
public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
结果:
Capital of India----Delhi
now hashcode for above two objects india1 and india2 are same, so Both will be point to same bucket,now equals method will be used to compare them which will return true.
if you override equals() method then you must override hashCode() method
总结:在hashmap比较key时,key对象(比如String Country等)的hashcode值相等时,再进行key对象的equals()方法的比较,若equals()相等,才能说明两个key一样。那么hashmap存的只有一个而不是两个。
扩展:
equals方法的四个特性
如果两个对象相等,那么他们必须含有相同的hashcode
如果两个对象的hashcode相等,他们的equals可能相等也可能不相等
HashMap----一个叫做table大小是16的Entry数组。
这个table数组存储了Entry类的对象。HashMap类有一个叫做Entry的内部类。这个Entry类包含了key-value作为实例变量。我们来看下Entry类的结构。Entry类的结构:
static class Entry implements Map.Entry { final K key; V value; Entry next; final int hash; ...//More code goes here }
每当往hashmap里面存放key-value对的时候,都会为它们实例化一个Entry对象,这个Entry对象就会存储在前面提到的Entry数组table中。现在你一定很想知道,上面创建的Entry对象将会存放在具体哪个位置(在table中的精确位置)。答案就是,根据key的hashcode()方法计算出来的hash值(来决定)。hash值用来计算key在Entry数组的索引。
我们往hashmap放了4个key-value对,但是看上去好像只有2个元素!!!这是因为,如果两个元素有相同的hashcode,它们会被放在同一个索引上。问题出现了,该怎么放呢?原来它是以链表(LinkedList)的形式来存储的(逻辑上)。
put方法的实现:
/** * Associates the specified value with the specified key in this map. If the * map previously contained a mapping for the key, the old value is * replaced. * * @param key * key with which the specified value is to be associated * @param value * value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or <tt>null</tt> * if there was no mapping for <tt>key</tt>. (A <tt>null</tt> return * can also indicate that the map previously associated * <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<k , V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
对key做null检查。如果key是null,会被存储到table[0],因为null的hash值总是0。
indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。
get方法:
/** * Returns the value to which the specified key is mapped, or {@code null} * if this map contains no mapping for the key. * * <p> * More formally, if this map contains a mapping from a key {@code k} to a * value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise it returns * {@code null}. (There can be at most one such mapping.) * * </p><p> * A return value of {@code null} does not <i>necessarily</i> indicate that * the map contains no mapping for the key; it's also possible that the map * explicitly maps the key to {@code null}. The {@link #containsKey * containsKey} operation may be used to distinguish these two cases. * * @see #put(Object, Object) */ public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
对key进行null检查。如果key是null,table[0]这个位置的元素将被返回。
key的hashcode()方法被调用,然后计算hash值。
indexFor(hash,table.length)用来计算要获取的Entry对象在table数组中的精确的位置,使用刚才计算的hash值。
在获取了table数组的索引之后,会迭代链表,调用equals()方法检查key的相等性,如果equals()方法返回true,get方法返回Entry对象的value,否则,返回null。
要牢记以下关键点:
参考文献:http://www.importnew.com/10620.html