您的位置:

Java中put方法的使用

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方法的使用。