自定義類作為Map key的條件
阿新 • • 發佈:2019-01-04
一,大家通常會用HashMap存放資料,key一般用的是String,Integer這種基本資料型別,但是用自定義物件或者泛型作為key的情況會出現put進去get不到的情形,如下程式碼:
public class MapKey { public static void main(String[] args) { class User { private long id; private String name; private int age; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public User(long id, String name, int age){ this.id = id; this.name = name; this.age = age; } } Map<User, String> map = new HashMap<>(); User user1 = new User(1l,"hu", 28); User user2 = new User(1l,"hu", 28); map.put(user1, "hujt"); System.out.println(user1.equals(user2)); System.out.println(map.get(user2)); }
false
null
輸出如上,false和null,就是說user1與user2不等,put進去的new User(1l, "hujt", 28),用new User(1l, "hujt", 28)取到的是空。為什麼呢?
二,看下HashMap get資料的原始碼如下:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
獲取key對應值就是對key進行hashcode,遍歷hashmap找到key的hashcode與前面的hashcode的值相等,且key的值本身也是相等。那為什麼兩個物件user1和user2的hashcode不相等呢?每個物件都繼承Objcet的hashCode方法,final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
由上可知hashCode()是虛擬機器本地方法,註釋中有一句話 (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java™ programming language.),意思是hashcode返回的值是根據實體地址轉成的整數,但是這不是java語言本身去實現的。那麼新生成的兩個物件的實體地址肯定不一樣,導致hashcode值不一致,所以取到的是空。所以這裡需要重寫hashCode()和equals()方法。程式碼如下/** As much as is reasonably practical, the hashCode method defined by * class {@code Object} does return distinct integers for distinct * objects. (This is typically implemented by converting the internal * address of the object into an integer, but this implementation * technique is not required by the * Java™ programming language.) * * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ public native int hashCode();
public class MapKey {
public static void main(String[] args) {
class User {
private long id;
private String name;
private int age;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(long id, String name, int age){
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return this.id + "," + this.name + "," + this.age;
}
@Override
public boolean equals(Object object) {
if(this == object) return true;
if(object instanceof User) {
User user = (User)object;
return this.id == user.id && this.name.equals(user.name) && this.age == user.age;
} else {
return false;
}
}
@Override
public final int hashCode() {
int hashcode = 17;
hashcode = hashcode * 31 + (int)this.id;
hashcode = hashcode * 31 + this.age;
return hashcode;
}
}
Map<User, String> map = new HashMap<>();
User user1 = new User(1l,"hu", 28);
User user2 = new User(1l,"hu", 28);
map.put(user1, "hujt");
System.out.println(user1.equals(user2));
System.out.println(map.get(user2));
}
上面程式碼返回true,“hujt",達到預期的結果。如果我們用同一個物件例項去put(user1,
"XXX"),再用get(user1)返回的結果就是“XXX”,效果一樣。