Skip to content
CodingDiary
返回

HashMap 的 JDK 源码探索

编辑页面
导读

Map.Entry 明明是个接口,为什么能直接调用 getKey() 和 getValue()?从一个朋友的疑问出发,深入 HashMap 源码,揭秘 entrySet() 返回的其实是内部类 EntrySet,而真正干活的是实现了 Map.Entry 的 Node 类。一次完整的 Java 多态实战分析,帮你理解接口、实现类、内部类之间的关系。

缘由:今天好友拿着下面的代码,问我为什么Map.Entry 这个接口没有实现 getKey() 和 getValue() 方法,却可以使用,由此,开启了一番查阅 JDK 源码的旅途…

Map map = new HashMap();

map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
map.put(4, "赵六");
map.put(5, "钱七");

Set set = map.entrySet();
for (Object object : set) {
   Map.Entry entry = (Map.Entry) object;
   System.out.println(entry.getKey() + "-->" + entry.getValue());
}
  1. 首先,我们看 map 对象,这个 map 对象是 HashMap 的一个实例,然后下面的 Set set = map.entrySet(); 可以知道这其实用的 HashMap 实现的 entrySet() 方法,然后我们可以查看 HashMapentrySet() 的源码

    从源码可以看出,这里的返回了一个 EntrySet 对象,但是需要注意的是这个 EntrySetHashMap 里的一个内部类,源码如下:

    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public final int size() {
           return size;
        }
    
        public final void clear() {
            HashMap.this.clear();
        }
    
        public final Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
    
        public final boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Node<K,V> candidate = getNode(hash(key), key);
            return candidate != null && candidate.equals(e);
        }
    
        public final boolean remove(Object o) {
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
                Object key = e.getKey();
                Object value = e.getValue();
                return removeNode(hash(key), key, value, true, true) != null;
            }
            return false;
        }
    
        public final Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
    
        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

    从这里我们是可以看出,这个 EntrySet 其实是封装的一个 Node 类的实体。也就是说我们的 set 其实就是这个 Node 对象。

  2. 现在我们来说说这个 Node 对象,Node 对象也是 HashMap 里的一个内部类,源码如下:

    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;
        }
    
        public final K getKey() {
            return key;
        }
    
        public final V getValue() {
            return value;
        }
    
        public final String toString() {
            return key + "=" + value;
        }
    
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
    
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
    
        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

    可以看出来,这个 Node 对象是 Map.Entry<K,V> 的实现类,我们可以看到这个 Node 对象实现了 getKey()getValue() 的方法,所以后面调用的 entry.getKey() 以及 entry.getValue() 方法其实都是调用的 Node 对象里的getKey()getValue() 方法,这里就是 Java 的多态的一种表现。

  3. 至此,打完收枪!


编辑页面
分享到:

上一篇
Windows10 系统下载与激活
下一篇
Oracle 11gR2 RAC的JDBC连接串