Java中的Map是一种非常常见的数据结构,其具有键值对的形式存储,通常用来存储一组相关的数据。而put方法是Map接口中的一种方法,该方法用来将一组键值对存储到Map中。在本篇文章中,我们将从多个方面介绍Java中put方法的使用。
一、put方法的基本使用
首先,我们来看一下put方法的基本使用:
Map<String, String> map = new HashMap<>(); map.put("name", "Tom"); map.put("age", "18");
上述代码中,我们首先定义了一个Map实例,然后使用put方法将两组键值对存储到Map中。在这里,我们传入的是一个String类型的键和一个String类型的值。需要注意的是,在使用put方法的时候,如果Map中已经存在了这个键,则会用新的值替换旧的值。
二、put方法的返回值
除了将一组键值对存储到Map中之外,put方法还可以返回该键对应的旧值。例如:
Map<String, String> map = new HashMap<>(); map.put("name", "Tom"); String oldValue = map.put("name", "Jerry"); System.out.println(oldValue); // 输出Tom
上述代码中,我们首先使用put方法将键为"name"、值为"Tom"的键值对存储到Map中。接着,我们再次使用put方法,将键为"name"、值为"Jerry"的键值对存储到Map中。注意到在此过程中,Map中名为"name"的键已经存在,因此使用新的值"Jerry"替换掉了旧值"Tom"。而此时,put方法返回的值就是被替换掉的旧值"Tom"。
三、put方法的默认实现
Map接口中提供了多个不同的实现类,如HashMap、TreeMap、ConcurrentHashMap等。而在这些实现类中,put方法的具体实现也是不同的。下面我们来详细介绍一下HashMap和TreeMap中put方法的默认实现。
1. HashMap中put方法的默认实现
HashMap是一种散列表,它采用了"拉链法"来解决哈希冲突。下面是HashMap中put方法的默认实现:
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { // 省略其他代码段 } modCount++; // 记录Map中实际存储的键值对数量 if (++size >= threshold) // 如果Map中的键值对数量超过了扩容阈值 resize(); // 则进行扩容 afterNodeInsertion(evict); return null; }
上述代码中,我们可以看到put方法的具体实现其实是调用了putVal方法,这个方法就是HashMap中put方法的具体实现。在这个方法中,我们可以看到,首先通过hash方法计算出了当前键对应的哈希值。接着,如果键对应的槽位没有被占用,就直接在该槽位中存储该键值对。否则,就需要对已经存在的键值对进行更新操作。最后,如果Map中的键值对数量超过了扩容阈值,就需要进行扩容操作。
2. TreeMap中put方法的默认实现
TreeMap是一种基于红黑树的映射。下面是TreeMap中put方法的默认实现:
public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; Comparator<? super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null; }
上述代码中,我们可以看到put方法的具体实现是直接在TreeMap中新建一个Entry实例,并将其插入到红黑树中。在实现中,首先通过比较器(如果存在)或者键实现Comparable接口的compareTo方法(如果键没有指定比较器并且实现了Comparable接口)来判断当前键应该插入到红黑树的哪个位置。接着,如果节点已经存在,则用新的值替换旧的值,否则就将新节点插入到红黑树中。最后,如果摆动操作遵循红黑树插入的规则。
四、put方法的使用技巧
做到了上述的put方法的基本使用、返回值、默认实现,接下来我们将介绍一些使用技巧。
1. 使用putIfAbsent方法避免键的重复插入
在实际开发中,有时候需要向Map中插入一组键值对,但是如果键已经存在,则不插入。这个需求可以通过putIfAbsent方法来实现。例如:
Map<String, String> map = new HashMap<>(); map.put("name", "Tom"); map.putIfAbsent("name", "Jerry"); System.out.println(map.get("name")); // 输出Tom
上述代码中,由于"Tom"已经被存储为"name"键的值,因此使用putIfAbsent方法插入"Jerry"时会失败,不会对Map做出任何修改。在输出时,我们可以发现"name"键的值仍然为"Tom"。
2. 使用putAll方法批量插入键值对
如果需要将一个Map中的所有键值对都插入到另一个Map中,那么可以使用putAll方法。例如:
Map<String, String> map1 = new HashMap<>(); map1.put("name", "Tom"); map1.put("age", "18"); Map<String, String> map2 = new HashMap<>(); map2.putAll(map1); System.out.println(map2.get("name")); // 输出Tom System.out.println(map2.get("age")); // 输出18
上述代码中,我们首先定义了map1,并向其中插入了两组键值对。接着,我们定义了一个空的map2,使用putAll方法将map1中的所有键值对都插入到map2中。最后,我们通过get方法检验了一下map2中存储的键值对是否正确。
3. 使用computeIfAbsent方法避免重复计算
在实际开发中,有时候需要根据键的值来计算一些数据,并将计算结果存储到Map中。如果键的值已经对应了一个存储的计算结果,那么就可以直接获取这个结果而不需要重复计算。这个需求可以使用computeIfAbsent方法来实现。例如:
Map<String, Integer> map = new HashMap<>(); map.put("count", 1); Integer result = map.computeIfAbsent("count", key -> computeCount(key)); System.out.println(result); // 输出1
上述代码中,我们首先定义了一个名为count的键,对应的值为1。接着,我们使用computeIfAbsent方法来获取该键对应的计算结果。在这里,computeIfAbsent方法会首先检查名为count的键是否存在。由于count键已经被存储在map中,因此computeIfAbsent方法不会对该键进行计算,而是直接返回其对应的值1。最后,我们通过输出检查一下返回值是否为1。
五、小结
本文主要介绍了Java中Map接口中的put方法。我们从put方法的基本使用、返回值、默认实现和使用技巧四个方面进行了详细的介绍。除此之外,还结合具体的代码实现,帮助读者更加深入地了解Java中put方法的使用。