Hash算法
在说HashMap之前先来了解一下Hash算法。在数据结构中学习过线性表,我们知道在线性表中查询一个值最坏的情况可能是从头遍历到尾,其平均时间复杂度为O(n),并不理想,而hash表能将查询的时间复杂度降为O(1),因为Hash算法会通过hash函数计算出地址直接取值,其查询次数只有一次。
通过下面例子简单了解一下hash表的查询方式,下面是一个hash表,首先假设hash函数为n%10,hash函数计算出来的结果就是其hash表中该元素的地址,所以10、13和26的存出结果如下图。
可能实际的hash函数能很好地避免地址的冲突,但是还是有地址冲突的可能性,比如10和20。java中hashMap解决方式是"链地址法",如下图,将hash值冲突的值使用链表连接起来,这样查询到地址0的时候就会依次比较10和20,看到底哪个才是要找的。
HashMap
下面来了解什么是HashMap,“Map集合即Key-Value的集合,前面加个Hash,即散列,无序的。所以HashMap即散着的,无序的Key-Value集合”,这是最初我看到的一个对我个人而言比较好理解的解释。当我们使用的hashMap的键值为对象的时候可能就要重写hashCode和eqals。
先看下面一段代码
public class User {
private String name;
private String password;
public User() {
super();
}
public User(String name, String passed) {
super();
this.name = name;
this.passed = passed;
}
public class Demo {
public static void main(String[] args) {
User user1=new User("name", "passed");
User user2=new User("name", "passed");
//定义hashMap
HashMap<User, String> hashmap=new HashMap<User,String>();
//添加
hashmap.put(user1, "value1");
//通过user2获取添加的value
String str=hashmap.get(user2);
System.out.println("输出结果:"+str);
}
}
通过代码可知,实体类User中只有两个属性name和password,Main函数中声明了两个User的实例,他们的两个属性都是相同的,那我们现在希望使用user2取出user1对应的value值,看下是否能成功。
运行结果:
输出结果:null
为什么不是想象的结果呢。因为当我们向hashmap添加user1时,hashmap首先会调用User的hashCode来计算hash值作为地址,因为本例中没有重写hashCode方法,所以hashmap是调用的Object的hashCode方法来计算hash值,Object中hashCode计算出来的hash值其实就是对象的地址,所以user1与user2存储的的地址肯定不同,下面就重写User的hashCode
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((passed == null) ? 0 : passed.hashCode());
return result;
}
运行结果:
输出结果:null
运行结果还是null,既然重写了hashCode方法,寻找user2时候理论上是能够正确寻找到user1存储地址的,为什么结果还是null?这里就要了解一下HashMap找到地址后动作。前面已经说过,java中HashMap解决hash值冲突,使用了链地址法,也就是在通过user2获取user1的value的时候并不是通过User重写的hashCode计算出user2的地址后就直接从该地址中取相应的值,而是还要调用equals方法来进行比较,这就和没有重写hashCode造成错误的原因类似了,没有重写equals方法,就要被迫调用Object类的equals方法,而Object类的equals方法是直接比较两个对象的内存地址,所以输出结果是null。
现在重写equals方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (passed == null) {
if (other.passed != null)
return false;
} else if (!passed.equals(other.passed))
return false;
return true;
}
运行结果:
输出结果:value1
成功。
总结
由上面的分析可知,当键值为对象类型的时候就需要重写hashCode和equals方法。