首页 > 编程笔记 > Java笔记

组合模式在JDK源码中的应用

HashMap 是我们使用频率最高的用于映射(键值对)处理的数据类型,本节来看组合模式在 HashMap 中的应用。

HashMap 类的部分源码如下。
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    ...
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    ...
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
    ...
}
以上是 HashMap 类的简化源码,我们可以看到 putAll( ) 方法中传入的是 Map 对象。这里的 Map 就是一个抽象构件,同时这个构件只支持键值对的存储格式,而 HashMap 是一个中间构件,HashMap 中的 Node 节点就是叶子节点。

说到中间构件就肯定会有规定的存储方式,HashMap 中的存储方式是一个静态内部类的数组 Node<K,V>[] tab,源码如下。
static class Node<K, V> implements Map.Entry<K, V> {
    final int hash;
    final K key;
    V value;
    Node<K, V> next;

    Node(int hash, K key, V value, Node<K, V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    ...

    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 {
            Node<K, V> e;
            K k;
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    ...
}
HashTable 也可以看作是一个中间构件,里面的 putAll 方法同样传入的是 Map 对象。关于 Map 接口的关系图如下:
Map接口关系图

同理,我们常用的 ArrayList 对象也有 addAll() 方法,其参数也是 ArrayList 的父类 Collection,源码如下。
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    ...

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    ...
}
组合对象和被组合对象应该有统一的接口实现或者统一的抽象父类。

学习完本篇文章后,可以再去回顾一下组合模式的定义和使用场景,说不定收获会更大。

所有教程

优秀文章