Java 基础 —— HashMap

Java 基础 —— HashMap

简介

HashMap 用于存放键值对,基于哈希表的Map接口实现,属于常用Java集合之一。

  • JDK1.8之前HashMap由 数组 + 链表 组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的的(“拉链法”解决冲突)
  • JDK1.8之后在解决哈希冲突时有较大改变,当链表长度大于阀值(默认为8)时,将链表转换为红黑树,以减少搜索时间。

底层数据结构分析

JDK1.8前

1.8前底层是数组和链表结合在一起,也就是链表散列

  • HashMap 通过 Key 的 hashCode经过扰动函数处理过后得到hash值(一个近似值),然后通过(n - 1) & hash判断当前元素存放的位置(这里的n指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同就通过链表法解决冲突。
    扰动函数: 就是HashMap的hash方法。使用 hash 方法即是扰动函数,是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。
JDK 1.8 HashMap的hash方法源码
/**
  * JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。
  * @param key
  * @return
  */
static final int hash(Object key) {
    int h;
    // key.hashCode():返回散列值也就是hashcode
    // ^ :按位异或
    // >>>:无符号右移,忽略符号位,空位都以0补齐
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
对比 JDK 1.7 HashMap的hash方法源码
static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
结论
  • 1.7的hash方法性能稍差一点点,因为扰动了4次
  • 拉链法
    将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
    在这里插入图片描述

JDK1.8后

相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阀值(默认为8)时,将链表转化为红黑树 ,以减少搜索时间。
在这里插入图片描述

类属性
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    // 序列号
    private static final long serialVersionUID = 362498820763181265L;    
    // 默认的初始容量是16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;   
    // 最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30; 
    // 默认的填充因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    // 当桶(bucket)上的结点数大于这个值时会转成红黑树
    static final int TREEIFY_THRESHOLD = 8; 
    // 当桶(bucket)上的结点数小于这个值时树转链表
    static final int UNTREEIFY_THRESHOLD = 6;
    // 桶中结构转化为红黑树对应的table的最小大小
    static final int MIN_TREEIFY_CAPACITY = 64;
    // 存储元素的数组,总是2的幂次倍
    transient Node<k,v>[] table; 
    // 存放具体元素的集
    transient Set<map.entry<k,v>> entrySet;
    // 存放元素的个数,注意这个不等于数组的长度。
    transient int size;
    // 每次扩容和更改map结构的计数器
    transient int modCount;   
    // 临界值 当实际大小(容量*填充因子)超过临界值时,会进行扩容
    int threshold;
    // 加载因子
    final float loadFactor;
}
loadFactor 加载因子
threshold
Node 节点类源码
// 继承自 Map.Entry<K,V>
static class Node<K,V> implements Map.Entry<K,V> {

	   // 哈希值,存放元素到hashmap中时用来与其他元素hash值比较
       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; }
        
        // 重写hashCode()方法
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        
        // 重写 equals() 方法
        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;
        }
}
树节点类源码
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    // 父
    TreeNode<K, V> parent;
    // 左
    TreeNode<K, V> left;
    // 右
    TreeNode<K, V> right;
    // needed to unlink next upon deletion
    TreeNode<K, V> prev;
    // 判断颜色
    boolean red;           

    TreeNode(int hash, K key, V val, Node<K, V> next) {
        super(hash, key, val, next);
    }

    // 返回根节点
    final TreeNode<K, V> root() {
        for (TreeNode<K, V> r = this, p; ; ) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }
}

基础关系图

版本:JDK1.8
在这里插入图片描述

HashMap 源码分析

继承关系

实现关系

内部类

Holder
Entry
HashIterator
ValueIterator
KeyIterator
EntryIterator
KeySet
Values
EntrySet

函数意义

HashMap(int, float)
HashMap(int)
HashMap()
HashMap(Map<? extends K, ? extends V>)
roundUpToPowerOf2
inflateTable
init
initHashSeedAsNeeded
hash
indexFor
size
isEmpty
get
getForNullKey
containsKey
getEntry
put
putForNullKey
putForCreate
putAllForCreate
resize
transfer
putAll
remove
removeEntryForKey
removeMapping
clear
containsValue
containsNullValue
clone
addEntry
createEntry
newKeyIterator
newValueIterator
newEntryIterator
keySet
values
entrySet
entrySet0
writeObject
readObject
capacity
loadFactor

静态常量

DEFAULT_INITIAL_CAPACITY
MAXIMUM_CAPACITY
DEFAULT_LOAD_FACTOR
EMPTY_TABLE
table
size
threshold
loadFactor
modCount
ALTERNATIVE_HASHING_THRESHOLD_DEFAULT
hashSeed
entrySet
serialVersionUID

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值