首先说一下,hashmap集合的本质,他的底层是一个一维数组,数组中的每个元素都是一个单链表,每个单链表都有节点node,每个节点都包含四个元素:
hash;
final K key;
V value;
Node<K,V> next; 看图:
hashmap的k部分其实存储在hashset集合;我们知道hashset集合是不能重复的,如果存放相同的值,后一个会把前一个覆盖掉,既然这样,也就是说hashmap也不能重复。
说一下hashmap的添加元素的原理:首先,你放进去一个键值对,假如是<1,“user>”,首先会调用对象1(Integer类型)的hashcode方法,获得到hashcode值,然后底层经过哈希算法转换成数组下标,假如数组下标是1,那么首先会查看下标1这里是不是空,如果是空,会放进去,不是空,就拿k与其中的(单链表种第一个节点)的k用equals比较(比较的是内容,所以一定要重写equals),如果比较到最后也没有返回true(都返回false),就加入到单链表末尾;如果中途返回了返回了true,那么这个k将会被覆盖。
重点来了
假设现在有如下代码:
在这里插入代码片
public class ColletionTest01 {
public static void main(String[] args) {
HashSet hashSet=new HashSet();
user user1=new user("luo");
user user2=new user("luo");
System.out.println(user1.equals(user2));
hashSet.add(user1);
hashSet.add(user2);
System.out.println(hashSet.size());
}
}
class user{
private String anme;
public user(String anme) {
this.anme = anme;
}
public boolean equals(Object obj) {
if(obj==null || !(obj instanceof user)) return false;
if(obj==this) return true;
user u=(user) obj;
return this.anme.equals(u.anme);
}
}
运行结果:
显然重写了user类的equals方法,按理说答案因该输出1(因为hashset不允许有重复的)但是却输出了2。
上面代码是hashset,因为对于hashmap来说,你放进去的k部分就是hashset,上面提到了。
原因:仅仅重写了equals方法,没有重写hashcode方法。首先两个对象调用hashcode方法,但是得到了不同的值,k部分转化为数组下标,既然数组下标都不相同了,那么两个对象肯定全放进去。都放进去肯定是不行的,因为hashmap不能重复!!
**
解决办法:将equals方法和hashcode方法都重写,这样的话,就拿上面的例子来说,重写过后的hashcode方法,如果k部分内容相同的话,就会转化为相同的数组下标,user1和user2对象的内容相同,他们俩在同一个数组下标上,也就是说他们俩在同一个单链表上,之后会调用equals方法进行内容的比较,当把user1放进去后,在放进user2过程中,会把user1覆盖掉,因为他们俩内容相同,equals方法重写过后,比较的是内容**
equals和hashcode都重写后的代码:
在这里插入代码片
public class ColletionTest01 {
public static void main(String[] args) {
HashSet hashSet=new HashSet();
user user1=new user("luo");
user user2=new user("luo");
System.out.println(user1.equals(user2));
hashSet.add(user1);
hashSet.add(user2);
System.out.println(hashSet.size());
}
}
class user{
private String anme;
public user(String anme) {
this.anme = anme;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
user user = (user) o;
return Objects.equals(anme, user.anme);
}
@Override
public int hashCode() {
return Objects.hash(anme);
}
}
结果:
重写过后就对了,有重复的会覆盖掉!
补充其实重写后的hashcode方法,里面调用了hash方法,参数是k部分的内容,下面是调用hash方法的源代码:
在这里插入代码片
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
可以看出来,valuse就是你传入的参数,如果k部分相同,就证明你传进去的values相同,那么得到的hashcode也会相同,转化的数组下标也相同,这样就不会有重复的了!