HashMap 使用Object 对象作为Key 时, 如果Key 对应的类,重写了 hashCode() 和 equals 方法,
则可能会导致使用 同一个类的不同实例对象, 去从map 中获取 值时,得到的是同一个值。
例子如下:
1. 先自定义一个类
仅有两个成员变量,都是String 类型。 并且重写其hashCode() 和 equals 方法。
其中
(1). 根据成员变量的值进行hashCode 计算,
(2). equals 方法 是比较两个对象的成员变量是否一样,一样则返回true
import java.util.Objects;
public class TargetItem {
String mId;
String mName;
public TargetItem(String id, String name) {
this.mId = id;
this.mName = name;
}
@Override
public String toString() {
return "TargetItem{" +
"mId='" + mId + '\'' +
", mName='" + mName + '\'' +
'}';
}
//2. hashCode 一样的情况下, 才会调用equals 比较,例如可以根据成员变量的值比较
@Override
public boolean equals(Object other) {
System.out.println("正在调用"+ mName+ " 的equals方法 ");
boolean ret;
//ret = (this == other); // 引用地址的比较,两个实例对象,必然返回false
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
TargetItem that = (TargetItem) other;
//ret = Objects.equals(mId, that.mId) && Objects.equals(mName, that.mName);
// 成员变量的值的比较,即使两个实例对象地址不一样,内容一样则返回true
ret = mId.equals(that.mId) && mName.equals(that.mName);
System.out.println("equals 返回结果=" + ret);
return ret;
}
// 1. 先要确定hashCode 一样才会调用 equals
@Override
public int hashCode() {
return Objects.hash(mId, mName);
}
}
2. 测试代码:
import java.util.HashMap;
public class MainTest {
public static void main(String args[]) {
System.out.println("========1. 创建实例对象==================");
TargetItem tom1 = new TargetItem("1", "Tom");
System.out.println("tom1=" + tom1 + ", hashcode=" + tom1.hashCode());
TargetItem tom2 = new TargetItem("1", "Tom");
System.out.println("tom2=" + tom2 + ", hashcode=" + tom2.hashCode());
System.out.println("tom1==tom2: " + (tom1 == tom2));
System.out.println();
System.out.println("========2.仅将 tom1 填入 HashMap==================");
HashMap hashMap = new HashMap();
hashMap.put(tom1, "我是Tom");
System.out.println();
System.out.println("========3.根据tom1 作为key值get========");
System.out.println("value: " + hashMap.get(tom1));
System.out.println();
System.out.println("========4.根据tom2 作为key值get ========");
System.out.println("value: "+hashMap.get(tom2));
}
}
输出结果:
> Task :javatest:MainTest.main()
========1. 创建实例对象==================
tom1=TargetItem{mId='1', mName='Tom'}, hashcode=86754
tom2=TargetItem{mId='1', mName='Tom'}, hashcode=86754
tom1==tom2: false
========2.仅将 tom1 填入 HashMap==================
========3.根据tom1 作为key值get========
value: 我是Tom
========4.根据tom2 作为key值get ========
正在调用Tom 的equals方法
equals 返回结果=true
value: 我是Tom
可以看到,tom1 / tom2 的地址值时不同的, 因此 (tom1== tom2) 返回false,
而根据tom2 去get 时, 返回的值是和使用tom1时 一样的值,因为tom1/tom2的hashCode()一样,并且它们类的equals 返回true.
查看HashMap的字节码文件,可以看出, HashMap 在进行get操作时,是会先根据传入的对象的hashCode() 值进行hash计算。
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
public V get(Object var1) {
HashMap.Node var2;
return (var2 = this.getNode(hash(var1), var1)) == null ? null : var2.value;
}
static final int hash(Object var0) {
int var1;
return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;
}
}