HashMap分析

本文详细介绍了HashMap,包括其简介、数据结构、JDK 8u201版本的源码解析,重点讲解了Node、内部集合类、Iterator、Spliterator和TreeNode。HashMap基于Map接口,实现 Cloneable 和 Serializable。其数据结构包含桶数组,每个桶可能是链表或红黑树。源码解析部分探讨了Node的链表结构、内部集合类的功能以及TreeNode的红黑树实现。

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

第1部分 HashMap介绍

HashMap简介

HashMap实现了Map接口,提供关于K,V和Entry的操作。
HashMap实现了Cloneable接口,提供了clone方法。
HashMap实现了Serializable接口,提供了序列化和反序列化方法。

HashMap构造函数

修饰语和返回类型 方法 描述
public HashMap(int initialCapacity, float loadFactor) 传入初始容量和加载因子
public HashMap(int initialCapacity) 传入初始容量
public HashMap() 默认构造方法
public HashMap(Map<? extends K, ? extends V> m) 根据m构造

HashMap常用API

修饰语和返回类型 方法 描述
int size() 元素数量
boolean isEmpty() 为空
V get(Object key) 根据key获取value
boolean containsKey(Object key) 包含key对应元素
V put(K key, V value) 添加元素
V remove(Object key) 移除key对应的元素
void clear() 清空
boolean containsValue(Object value) 包含value
Set<K> keySet() 获取key集合
Collection<V> values() 获取value集合
Set<Map.Entry<K,V>> entrySet() 获取entry集合

第2部分 HashMap数据结构

HashMap的继承关系

java.lang.Object
   ↳     java.util.AbstractMap<K,V>
         ↳     

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {}

HashMap的关系图
在这里插入图片描述
图1 HashMap的关系图

其中table是一个桶数组,其每个桶可以是一条单向链表,也可以是一个即是双向链表,又是红黑树的复合结构,下文简称红黑树。

在这里插入图片描述
在这里插入图片描述
注意是每个桶都可以是这两种结构,而不是整个HashMap构成一个红黑树。

第3部分 HashMap源码解析(基于JDK-8u201)

内部类Node

Node是构成HashMap的基本结点,是组成每个桶中链表的基本元素,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; }
	//哈希值为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;
        //否则,要求是Map.Entry类型,并且K,V分别等价
        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结点的方法比较简单,重点关注其结构,也就是链表
在这里插入图片描述
图2 Node的链表结构

三个内部集合类

KeySet ,Values ,EntrySet这三个主要都是提供内部元素的迭代,因此放一起看。

final class KeySet extends AbstractSet<K> {
   
    public final int size()                 {
    return size; }
    public final void clear()               {
    HashMap.this.clear(); }
    //获取KeyIterator迭代器,这个迭代器在后面说,
    public final Iterator<K> iterator()     {
    return new KeyIterator(); }
    public final boolean contains(Object o) {
    return containsKey(o);}
    //调用HashMap的removeNode方法
    public final boolean remove(Object key) {
   
        return removeNode(hash(key), key, null, false, true) != null;
    }
    //获取可分割迭代器,JDK8新增
    public final Spliterator<K> spliterator() {
   
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    //Collection接口提供的方法,JDK8新增
    public final void forEach(Consumer<? super K> action) {
   
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        //HashMap中有元素,并且将table的引用传给tab遍历
        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.key);
            }
            //fast-fail机制,确保迭代过程中没有被修改
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

final class Values extends AbstractCollection<V> {
   
    public final int size()                 {
    return size; }
    public final void clear()               {
    HashMap.this.clear(); }
    public final Iterator<V> iterator()     {
    return new ValueIterator(); }
    public final boolean contains(Object o) {
    return containsValue(o); }
    public final Spliterator<V> spliterator() {
   
        return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer<? super 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.value);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

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();
        //在hash(key)的桶中,找到key对应的结点,这样只需要遍历一条链表
        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();
        }
    }
}

上面三个集合特别像,主要都是提供两种迭代器和forEach方法,后面将介绍这两种迭代器。

内部类Iterator

/*
提供后面三种Iterator迭代的抽象,实现大部分方法
注意其内部并没有提供抽象方法,也没有实现Iterator接口,
但是实现了除next外的所有方法,nextNode返回的是Node(也就是Entry)结点
*/
abstract class HashIterator {
   
    Node<K,V> next; //要迭代的下一结点,next时使用    
    Node<K,V> current;//当前节点,删除时使用    
    int expectedModCount;// fast-fail机制
    int index;//桶的索引             

    HashIterator() {
   
        expectedModCount = modCount;
        Node<K,V>[] t = table;//将HashMap对象的数据传进来
        current = next = null;
        index = 0;
        //如果有元素
        if (t != null && size > 0) {
    // advance to first entry
        	//找到第一个链表非空的桶,并将next指向该链表的头
            do {
   } while (index < t.length && (next = t[index++]) == null);
        }
    }
    public final boolean hasNext() {
   
        return next != null;
    }
	//获取下一个元素
    final Node<K,V> nextNode() {
   
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        /*
        如果e也就是原先的next非空,直接把这个"next"返回,并且next后移
      	否则,查找下一个链表非空的桶,并将next指向该链表的头
		*/    
        if ((next = (current = e).next) == null && (t = table) != null) {
   
            do {
   } while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }
	//移除current元素
    public final void remove() {
   
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        //根据current的key调用HashMap的removeNode方法
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}
//KeyIterator,next方法具体方法的key,其他Iterator接口中的方法在其父类中实现
final class KeyIterator extends HashIterator
    implements Iterator<K> {
   
    public final K next() {
    return nextNode().key; }
}

final class ValueIterator extends HashIterator
    implements Iterator<V> {
   
    public final V next() {
    return nextNode().value; }
}

final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
   
    public final Map.Entry<K,V> next() {
    return nextNode(); }
}

从上面的设计中,可以看出模板设计模式的影子,抽象父类将大部分方法实现,子类只需要提供简单的具体实现。虽然这不是模板设计模式,但是在平时写代码的过程中,也要尽量将重复的部分进行提取,避免重复造轮子。

内部类Spliterator

static class HashMapSpliterator<K,V> {
   
    final HashMap<K,V> map; 
    Node<K,V> current;   //当前节点
    int index;    		//当前索引,在advance/split中会修改           
    int fence;          //最后一个索引,也就是桶数组的长度
    int est;            //评估容量
    int expectedModCount;    

    HashMapSpliterator(HashMap<K,V> m, int origin,
                       int fence, int est,
                       int expectedModCount) {
   
        this.map = m;
        this.index = origin;
        this.fence = fence;
        this.est = est;
        this.expectedModCount = expectedModCount;
    }
	//初始化最后一个索引
    final int getFence() {
    // initialize fence and size on first use
        int hi;
        /*第一次使用时fence<0才会调用,例如
        KeySpliterator<>(HashMap.this, 0, -1, 0, 0),
        传入的fence为-1,需要初始化
        */
        if ((hi = fence) < 0) {
   
            HashMap<K,V> m = map;
            est = m.size;
            expectedModCount = m.modCount;
            Node<K,V>[] tab = m.table;
            //获取长度
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        return hi;
    }
	//获取预估容量,也就是HashMap的Size
    public final long estimateSize() {
   
        getFence(); // force init
        return (long) est;
    }
}

static final class KeySpliterator<K,V>
    extends HashMapSpliterator<K,V>
    implements Spliterator<K> {
   
    KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
                   int expectedModCount) {
   
        super(m, origin, fence, est, expectedModCount);
    }
	//尝试切分
    public KeySpliterator<K,V> trySplit() {
   
    	//将桶数组范围切半
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid || current != null) ? null :
        	//预估容量也切半,注意,这个数据是不准确的,只是个大概
            new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
                                    expectedModCount);
    }
	//对[index,fence]
    public void forEachRemaining(Consumer<? super K> action) {
   
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        //初始化
        if ((hi = fence) < 0) {
   
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        //i的范围[index,fence),index改为fence,也就是结尾
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
   
            Node<K,V> p = current;
            current = null;
            do {
   
            	//p == null,一般是到了链表的表尾
                if (p == null)
                    p = tab[i++];//下个桶的链表头
                else {
   
                    action.accept(p.key);
                    p = p.next;//指向链表的下一个结点
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
	//消费单个元素
    public boolean tryAdvance(Consumer<? super K> action) {
   
        int hi;
        if (action == null)
            throw new NullPointerException();
        Node<K,V>[] tab = map.table;
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
   
        	
            while (current != null || index < hi) {
   
                if (current == null)
                    current = tab[index++];//到链表尾,桶索引+1
                else {
   
                    K k = current.key;
                    //后移
                    current = current.next;
                    action.accept(k);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }
	//特征码
    public int characteristics() {
   
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
            Spliterator.DISTINCT;
    }
}
//后面两个可分割迭代器与key的差不多,就不分析了
static final class ValueSpliterator<K,V>
    extends HashMapSpliterator<K,V>
    implements Spliterator<V> {
   
    ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
                     int expectedModCount) {
   
        super(m, origin, fence, est, expectedModCount);
    }

    public ValueSpliterator<K,V> trySplit() {
   
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid || current != null) ? null :
            new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
                                      expectedModCount);
    }

    public void forEachRemaining(Consumer<? super V> action) {
   
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        if ((hi = fence) < 0
HashMapJava 中非常重要的数据结构之一,它实现了 Map 接口,提供了快速的键值对的查找和存储功能。下面是 HashMap 的源码分析: 1. 数据结构 HashMap 内部实现了一个数组,每个数组元素是一个单向链表,称为桶(bucket)。当我们向 HashMap 中添加一对键值对时,会根据键的哈希值(hashcode)计算出该键值对应该存储在哪个桶中。如果该桶中已经有了该键值对,就将该键值对添加到桶的末尾(Java 8 中是添加到桶的头部),否则就创建一个新的节点添加到桶的末尾。 2. 哈希冲突 如果两个键的哈希值相同,就称为哈希冲突。HashMap 采用链表法解决哈希冲突,即将哈希值相同的键值对存储在同一个桶中,通过单向链表组织起来。当我们根据键查找值时,先根据键的哈希值找到对应的桶,然后遍历该桶中的链表,直到找到目标键值对或者链表为空。 3. 扩容机制 当 HashMap 中的键值对数量超过了桶的数量的时候,就需要对 HashMap 进行扩容。扩容会重新计算每个键值对的哈希值,并将它们存储到新的桶中。Java 8 中,HashMap 的扩容机制发生了一些变化,采用了红黑树等优化方式。 4. 线程安全 HashMap 是非线程安全的,如果多个线程同时操作同一个 HashMap,就有可能导致数据不一致的问题。如果需要在多线程环境下使用 HashMap,可以使用 ConcurrentHashMap。 以上就是 HashMap 的源码分析,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值