java HashMap插入重复Key值问题

文章目录

要在HashMap中插入重复的值,首先需要弄清楚HashMap里面是怎么存放元素的。

put方法

Map里面存放的每一个元素都是key-value这样的键值对,而且都是通过put方法进行添加的,而且相同的key在Map中只会有一个与之关联的value存在。put方法在Map中的定义如下。

V put(K key, V value);
put()方法实现:首先hash(key)得到key的hashcode(),hashmap根据获得的hashcode找到要插入的位置所在的链,在这个链里面放的都是hashcode相同的Entry键值对,在找到这个链之后,会通过equals()方法判断是否已经存在要插入的键值对,而这个equals比较的其实就是key。

它用来存放key-value这样的一个键值对,返回值是key在Map中存放的旧value,如果之前不存在则返回null。HashMap的put方法是这样实现的。

put方法

当put一对<key,value>时,先用hashCode() && equals() 判断key是否重复
如果重复则不插入
如果hashCode()为true,则会用hashCode判断在相应的位置插入key,如果这个判断的位置已经有了值了,则会调用equals()去判断是否重复,equals()为true则视为key重复不插入,若equals()为false则认为不重复,以链的形式插入

// 在此映射中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换
public V put(K key, V value) { 
    // 当key为null,调用putForNullKey方法,保存null与table第一个位置中,这是HashMap允许为null的原因 
    if (key == null)
        return putForNullKey(value);
    // 使用hash函数预处理hashCode,计算key的hash值 
    int hash = hash(key.hashCode());//-------(1)
    // 计算key hash 值在 table 数组中的位置 
    int i = indexFor(hash, table.length);//------(2)
    // 从i出开始迭代 e,找到 key 保存的位置
    for (Entry<K, V> e = table[i]; e != null; e = e.next) { 
        Object k;
        // 判断该条链上是否有hash值相同的(key相同) 
        // 若存在相同,则直接覆盖value,返回旧value 
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 
            // 旧值 = 新值 
            V oldValue = e.value;
            // 将要存储的value存进去
            e.value = value;
            e.recordAccess(this);
            // 返回旧的value
            return oldValue;
        }
    }
    // 修改次数增加1 
    modCount++;
    // 将key、value添加至i位置处 
    addEntry(hash, key, value, i);
    return null;
}


从上我们可以看到在添加对应的key-value这样的组合时,如果原本已经存在对应的key,则直接改变对应的value,并返回旧的value,而在判断key是否存在的时候是先比较key的hashCode,再比较相等或equals的。
直接从上面代码来看是比较的对应Map.Entry的hashCode和key的hashCode,而实际上Map.Entry的hashCode其实就是其存放key的hashCode。而如果对应的key原本不存在的话将调用addEntry将对应的key-value添加到Map中。addEntry传递的参数hash就是对应key的hashCode。

 
自定义一个类型

class MyType { 

    private String arga;

    private String argb;


    public MyType(String arga, String argb) { 

        this.arga = arga;

        this.argb = argb;

    }

}

 
如果代码中用到类似HashMap<MyType, String> hm = new HashMap<MyType, String>();

那么定义两个变量  MyType mta = new MyType("aaa", "bbb");

                 MyType mtb = new MyTypr("aaa", "bbb");

                 hm.put(mta, "xxx");

                 hm.put(mtb, "xxx");

猜下HashMap中有几个元素?

 

答案是有两个,原因是mta和mtb放在了不同的内存地址里面,mta和mtb传进去的是引用,他们的哈希值是不同的

 
    /** * 既然提到了map的key的比较,再说一下map中实现自定义类做key值时应该注意的一些细节, * 在HashMap中对于key的比较时通过两步完成的 * 第一步:计算对象的hash Code的值,比较是否相等 * 第二步: 检查对应的hash code对应位置的对象是否相等 * 在第一步中会调用到对象中的hashCode()方法,第二步中会调用的对象中的equals()方法 * * 所以想要实现自定义对象作为Map的key值,保证key值的唯一性,需要在子定义对象中重写以上两个方法,如以下对象: */

那么怎么样实现HashMap没有值相同的Key呢?

方法很简单:只需要重写两个函数 public boolean equals(Object obj); 和 public int hashCode()

实现引用对象作为keys的唯一性

通过对put()方法的研究,我们可以发现,判断key是否存在的时候是先比较key的hashCode,再比较相等或equals的,所以重写hashCode()和equals()方法即可实现覆盖keys的引用(指向具有相同实例变量的对象)。

@Override
public int hashCode(){                  
     return this.arga.hashCode() * this.argb.hashCode() ; 
} 
 
@Override
public boolean equals(Object obj) {    
    if (this == obj) {                
        return true;                  
    }         
    if (!(obj instanceof MyType)) {   
        return false;               
    }    
    MyType p = (MyType) obj;  
    if (this.arga.equals(p.arga) && this.argb.equals(p.argb)) {               
        return true ;                  
    } else {            
        return false ;                
    }       
}

重写这两个方法之后就可以覆盖重复的键值对,如果需要对value进行叠加,调用put()方法之前用containsKey()方法判断是否有重复的键值,如果有,则用get()方法获取原有的value,再加上新加入的value即可。

get方法

首先得到key的hashCode()去得到值存的位置,然后用equals()去一个一个比较,为true则就是相应的value,否则不是

    原文作者:Kevin-Zeng
    原文地址: https://blog.csdn.net/weixin_38927257/article/details/102899226
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞