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方法是这样实现的。

// 在此映射中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换
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。
实现引用对象作为keys的唯一性
通过对put()方法的研究,我们可以发现,判断key是否存在的时候是先比较key的hashCode,再比较相等或equals的,所以重写hashCode()和equals()方法即可实现覆盖keys的引用(指向具有相同实例变量的对象)。

class MyType {
    private String arga;
    private String argb;

    public MyType(String arga, String argb) {
        this.arga = arga;
        this.argb = argb;
	}

	@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即可。

文章参考自:https://my.oschina.net/elim1/blog/811867
http://blog.csdn.net/dajian790626/article/details/13628137

<think>嗯,用户想知道Java HashMap在新增key时,如果发生重复会如何处理。我需要仔细回忆一下HashMap的工作原理。首先,HashMap是基于哈希表的Map接口实现,它存储键对,并且允许使用null键和null。当用户调用put方法添加一个键对时,如果该键已经存在,会发生什么呢? 根据官方文档的说法,HashMapput方法在遇到重复key时,会用新的value替换旧的value,并返回旧的value。这一点很重要,因为这确保了每个keyHashMap中是唯一的。例如,如果先put("a", 1),再put("a", 2),那么第二个操作会覆盖第一个,最终map中的value是2。 接下来,用户可能想知道这个覆盖行为是如何在源码中实现的。我记得HashMap内部使用了一个数组来存储节点,每个节点可能是一个链表或红黑树的结构,这取决于Java版本。当put操作发生时,会计算key的哈希,找到对应的数组下标。如果该位置已经有元素,就会遍历链表或树,检查是否有相同的key(通过equals方法)。如果找到相同的key,则替换value;否则,添加新的节点。 查看引用中的代码示例,用户提供的代码里有一个HashMap实例,先put("java",10),再put("php",10),然后再次put("java",20),这时候输出的map中java对应的应该是20,说明后面的put覆盖了前面的。这和文档中的描述一致。 另外,用户提到的引用[3]中遇到的问题,是在使用HashMap插入重复key,导致数据库主键冲突。这说明虽然HashMap内部处理了重复key的覆盖,但在实际应用中,如果用户依赖HashMap来管理唯一键,可能需要额外注意,比如使用putIfAbsent方法或者在数据库层处理唯一性约束。 还有,官方文档提到在put操作时,如果重映射函数修改了映射,可能会抛出ConcurrentModificationException异常。这通常发生在多线程环境下,或者在使用迭代器遍历时修改了HashMap的结构,导致并发修改异常。因此,使用时需要注意线程安全问题,或者使用ConcurrentHashMap来替代。 总结一下,当向HashMap插入重复key时,新value会覆盖旧value,并返回旧value。这个行为在官方文档中有明确说明,并且可以通过源码分析来验证。用户在实际开发中可以利用这一特性来更新数据,但也要注意可能引发的并发问题或数据覆盖带来的副作用。</think>### Java HashMap Key重复时的覆盖行为解析 在Java中,HashMap的键(Key)唯一性是其核心特性之一。**当新增重复Key时,HashMap会用新的Value覆盖旧的Value,并返回旧的Value**。此行为在官方文档中明确说明[^1][^4]。 #### 1. 官方文档说明 根据Java官方文档: > - `V put(K key, V value)`方法将指定的Key和Value关联到映射中。**如果映射中已包含该Key,则替换旧的Value**。 > - 返回是被替换的旧Value(如果Key不存在则返回`null`)。 例如: ```java HashMap<String, Integer> map = new HashMap<>(); map.put("java", 10); // 返回null(Key不存在) map.put("java", 20); // 返回10(旧Value被替换) ``` #### 2. 源码实现分析 在HashMap的`put`方法中,覆盖行为通过以下步骤实现[^4]: 1. **计算Key的哈希**:确定Entry在数组中的位置。 2. **遍历链表/红黑树**:检查该位置是否存在相同Key(通过`equals()`方法判断)。 3. **覆盖或新增**: - 若找到相同Key:替换旧Value,返回旧Value。 - 若未找到相同Key:创建新节点,返回`null`。 以引用[2]中的代码为例: ```java map.put("java", 10); map.put("php", 10); map.put("java", 20); // 覆盖"java"对应的Value为20 ``` 最终输出结果为`map={java=20, php=10}`,验证了覆盖行为。 #### 3. 实际应用中的注意事项 - **主键冲突问题**:如引用[3]所述,若将HashMapKey直接用作数据库主键,需确保Key的唯一性生成逻辑,避免覆盖行为导致数据异常。 - **线程安全性**:HashMap非线程安全,多线程环境下覆盖操作可能引发`ConcurrentModificationException`[^5]。 - **替代方法**: - `putIfAbsent()`:仅在Key不存在时插入。 - `compute()`:通过函数式接口控制覆盖逻辑。 ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值