HashMap源码分析

本文详细分析了HashMap的源码,从构造函数到put方法,再到inflateTable方法和EntrySet类。HashMap的容量初始化、负载因子设置、以及EntrySet的迭代器实现等关键点被逐一探讨,揭示了HashMap高效但非线程安全的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章我们来分析一下HashMap的源码实现,进一步阅读之前强烈建议先浏览一下之前文章《《Java Generics and Collections》笔记-Lists/Maps 》中关于Maps的部分。

文件位置:jdk/src/share/classes/java/util/HashMap.java
先看一下它的继承关系
  • java.lang.Object
    • java.util.AbstractMap<K,V>
      • java.util.HashMap<K,V>
可以看到HashMap没有继承Collection,而List、Queue、Set则是继承自Collection的。
 149     static final Entry<?,?>[] EMPTY_TABLE = {};
 150
 151     /**
 152      * The table, resized as necessary. Length MUST Always be a power of two.
 153      */
 154     transient Entry<K,V>[] table = (Entry<K,V>[])EMPTY_TABLE;

这里涉及到强制类型转换的问题,我们首先来看下面的例子:
15 class Base{}
16 class Derived extends Base{
18 }
  7     Base b = new Derived();
  8     Derived d = (Derived)b;
  9
 10     Base bs = new Base();
 11     Derived d2 = (Derived)bs;
运行时异常: java.lang.ClassCastException: Base cannot be cast to Derived
子类转换为父类是可以直接转换的,因为子类的内存模型里面包含了父类的部分。父类强制转换为子类要分情况,如果父类的实际类型也是父类,那么是不可以的,因为根据内存模型它的实际内存就只有父类的那么大,不能扩展到下面。但是如果实际类型是子类,那么就很容易转为子类了。
对于这里的泛型道理也是一样的,但是要正确判断泛型的父类与子类。比如HashSet<Object>与HashSet<Integer>不满足父子关系,Set<Object>与HashSet<Object>则满足父子类关系。
对于数组类型的泛型,我们知道对于普通类型Father[]是Son[]的父类。对于泛型,List<Father>[]就不是List<Son>[]的父类。至于为什么泛型作此规定可以从文章《Java泛型总结》中2.1小节得到答案。
154行把Entry<?,?>[]强制转为Entry<K,V>[],后者是前者的子类,把父类强制转为子类,只有当父类实际类型是子类的时候才允许,这里由于泛型擦出的原因是满足的。
这里的table就是用来保存实际的数据的,它的元素类型为Entry,接下来我们看一下Entry的结构:
805     static class Entry<K,V> implements Map.Entry<K,V> {
 806         final K key;
 807         V value;
 808         Entry<K,V> next;
 809         int hash;
 810
 811         /**
 812          * Creates new entry.
 813          */
 814         Entry(int h, K k, V v, Entry<K,V> n) {
 815             value = v;
 816             next = n;
 817             key = k;
 818             hash = h;
 819         }
 820
 821         public final K getKey() {
 822             return key;
 823         }
 824
 825         public final V getValue() {
 826             return value;
 827         }

 829         public final V setValue(V newValue) {
 830             V oldValue = value;
 831             value = newValue;
 832             return oldValue;
 833         }

 835         public final boolean equals(Object o) {
 836             if (!(o instanceof Map.Entry))
 837                 return false;
 838             Map.Entry e = (Map.Entry)o;
 839             Object k1 = getKey();
 840             Object k2 = e.getKey();
 841             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
 842                 Object v1 = getValue();
 843                 Object v2 = e.getValue();
 844                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
 845                     return true;
 846             }
可以看到,这里如果key和value都为null,或者key都为null,value相等的话也返回true。
 847             return false;
 848         }
 850         public final int hashCode() {
 851             return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
 852         }
 853
 854         public final String toString() {
 855             return getKey() + "=" + getValue();
 856         }
 872 }
现在我们把table的结构图示如下:


接下来我们看一下HashMap的构造方法:

250     public HashMap(int initialCapacity, float loadFactor) {
 251         if (initialCapacity < 0)
 252             throw new IllegalArgumentException("Illegal initial capacity: " +
 253                                                initialCapacity);
 254         if (initialCapacity > MAXIMUM_CAPACITY)
 255             initialCapacity = MAXIMUM_CAPACITY;
 256         if (loadFactor <= 0 || Float.isNaN(loadFactor))
 257             throw new IllegalArgumentException("Illegal load factor: " +
 258&n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值