分析HashMap 的 JDK 源码

缘由:今天好友拿着下面的代码,问我为什么 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() 方法,然后我们可以查看 HashMap 里 entrySet() 的源码

从源码可以看出,这里的返回了一个 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.至此,打完收枪!

以上就是分析HashMap 的 JDK 源码的详细内容,更多关于HashMap 的 JDK 源码的资料请关注我们其它相关文章!

(0)

相关推荐

  • jdk源码阅读Collection详解

    见过一句夸张的话,叫做"没有阅读过jdk源码的人不算学过java".从今天起开始精读源码.而适合精读的源码无非就是java.io,.util和.lang包下的类. 面试题中对于集合的考察还是比较多的,所以我就先从集合的源码开始看起. (一)首先是Collection接口. Collection是所有collection类的根接口;Collection继承了Iterable,即所有的Collection中的类都能使用foreach方法. /** * Collection是所有collec

  • 通过JDK源码角度分析Long类详解

    概况 Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其他类型之间的转换方法.除此之外还有一些位相关的操作. Java long数据类型 long数据类型是64位有符号的Java原始数据类型.当对整数的计算结果可能超出int数据类型的范围时使用. long数据类型范围是-9,223,372,036,854,775,808至9,223,372,036,85

  • Java基于JDK 1.8的LinkedList源码详析

    前言 上周末我们一起分析了ArrayList的源码并进行了一些总结,因为最近在看Collection这一块的东西,下面的图也是大致的总结了Collection里面重要的接口和类,如果没有意外的话后面基本上每一个都会和大家一起学习学习,所以今天也就和大家一起来看看LinkedList吧! 2,记得首次接触LinkedList还是在大学Java的时候,当时说起LinkedList的特性和应用场景:LinkedList基于双向链表适用于增删频繁且查询不频繁的场景,线程不安全的且适用于单线程(这点和Ar

  • Java从JDK源码角度对Object进行实例分析

    Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类.比如你随便创建一个classA,虽然没有明说,但默认是extendsObject的. 后面的三个点"..."表示可以接受若干不确定数量的参数.老的写法是Objectargs[]这样,但新版本的java中推荐使用...来表示.例如 publicvoidgetSomething(String...strings)(){} object是java中所有类的父类,也就是说所有的类,不管是自己创建的类还是

  • 源码解析JDK 1.8 中的 Map.merge()

    Map 中ConcurrentHashMap是线程安全的,但不是所有操作都是,例如get()之后再put()就不是了,这时使用merge()确保没有更新会丢失. 因为Map.merge()意味着我们可以原子地执行插入或更新操作,它是线程安全的. 一.源码解析 default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNu

  • 设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解

    组合模式及其在JDK源码中的运用 前言组合和聚合什么是组合模式示例透明组合模式透明组合模式的缺陷安全组合模式 组合模式角色组合模式在JDK源码中的体现组合模式应用场景享元模式优缺点总结 前言 本文主要会讲述组合模式的用法,并会结合在JDK和MyBatis源码中的运用来进一步理解组合模式. 在编码原则中,有一条是:多用组合,少用继承.当然这里的组合和我们今天要讲的组合模式并不等价,这里的组合其实就是一种聚合,那么聚合和组合有什么区别呢? 组合和聚合 人在一起叫团伙,心在一起叫团队.用这句话来诠释组

  • jdk动态代理源码分析过程

    代理对象的生成方法是: Proxy.newProxyInstance(...) ,进入这个方法内部,一步一步往下走会发现会调用 ProxyGenerator.generateProxyClass() ,这个方法用来生成代理类的字节码. 下面通过调用 ProxyGenerator.generateProxyClass()方法在本地生成代理类. 1.首先要有一个接口 2.生成代理类的方法如下 3.将生成的代理类导入到idea中查看是长这样 // // Source code recreated fr

  • 基于jdk1.8的Java源码详解 Integer

    public final class Integer extends Number implements Comparable<Integer> Integer 由final修饰了,所以该类不能够被继承,同时 Integer 继承了Number类,因此可以将Integer转换成 int .double.float.long.byte和short类型的数据,另外,也实现了comparable接口,因此Integer类也可以进行自然排序. 构造方法只有两个: public Integer(int

  • JDK源码分析之String、StringBuilder和StringBuffer

    前言 本文主要介绍了关于JDK源码分析之String.StringBuilder和StringBuffer的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 String类的申明 public final class String implements java.io.Serializable, Comparable<String>, CharSequence {-} String类用了final修饰符,表示它不可以被继承,同时还实现了三个接口, 实现Serializa

  • JDK源码中一些实用的“小技巧”总结

    前言 这段时间比较闲,就看起了jdk源码.一般的一个高级开发工程师, 能阅读一些源码对自己的提升还是蛮大的.本文总结了一些JDK源码中的"小技巧",分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1 i++ vs i-- String源码的第985行,equals方法中 while (n--!= 0) { if (v1[i] != v2[i]) return false; i++; } 这段代码是用于判断字符串是否相等,但有个奇怪地方是用了i--!=0来做判断,我们通

随机推荐