【java】HashMap1.7源码详解

前言

HashMap是java api中一个非常强大的工具类,所以剖析它的源码就显得很有必要了;强调,本篇博文面向的是熟知HashMap基本使用的编程者。

数据结构

在jdk1.7中HashMap的基本数据结构是数组+链表的形式。
在这里插入图片描述
使用过HashMap的同学都知道,它可以保存一对<key, value>,所以又是怎么放进数组的呢?答案是,在HashMap中有一个内部类Entry,每添加一个新的<key, value>就将它们封装到一个Entry对象中,然后放进容器中,当然,放入容器的过程不会这么简单。

static class Entry<K,V> implements Map.Entry<K,V> {
	// key不能变,很好理解,key是唯一的,只要保存在容器中,就不允许改变
    final K key;
    V value;
    // 维护链表,从此处看HashMap维护的是一条单向链表
    Entry<K,V> next;
    // key对应的hash值,用来得到存放数组中的下标
    int hash;

	// 构造方法,将新的Entry对象链接到当前Entry节点的后面
    Entry(int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }

    public final K getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    public final boolean equals(Object o) {
    	// 类型不符合,没有必要进行后面的操作
        if (!(o instanceof Map.Entry))
            return false;
        // 强转
        Map.Entry e = (Map.Entry)o;
        Object k1 = getKey();
        Object k2 = e.getKey();
        // 比较key和value,不一定是同一对象,只要满足key和value各自的equals()方法即可
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {
            Object v1 = getValue();
            Object v2 = e.getValue();
            if (v1 == v2 || (v1 != null && v1.equals(v2)))
                return true;
        }
        return false;
    }

    public final int hashCode() {
        return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
    }

    public final String toString() {
        return getKey() + "=" + getValue();
    }
	
	// HashMap.Entry并未具体实现这两个方法
    void recordAccess(HashMap<K,V> m) {}
    void recordRemoval(HashMap<K,V> m) {}
}

属性

// 默认数组容量,采用位运算提高速度,把16转化为二进制码相比于1 << 4要慢
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 数组的最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认的负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 空数组,在数组进行初始化时会使用到
static final Entry<?,?>[] EMPTY_TABLE = {};
// 此table数组用来存储Entry对象,是HashMap保存数据最关键的一步
// transient关键字的作用是:不参与对象的序列化
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
// 表中已存在的<key, value>个数(数组+链表)
transient int size;
// 数组扩容的临界值:threshold = loadFactor * capacity
int threshold;
// 加载因子
final float loadFactor;
// 容器的修改次数:当增加新数据(不代表修改value),删除数据,或者清空使modCount++
// 这个数据关乎到ConcurrentModificationException异常,会在后面进行讲解
transient int modCount;
// 映射容量的默认阈值,高于默认值时,散列度会降低,需要选择新的hash表
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

构造方法

public HashMap(int initialCapacity, float loadFactor) {
	// 容量不能小于0
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    // 容量不能大于最大值 1 << 30
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    // 加载因子不能小于等于0或者为非数字
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);

    this.loadFactor = loadFactor;
    // 临界值临时被赋值为指定容量,后面会重新计算
    threshold = initialCapacity;
    // 被LinkedHashMap重写,在HashMap中没有真正实现
    init();
}
// 使用默认的加载因子 
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// 使用默认的容量和加载因子
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
// 
public HashMa
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值