HashMap源码分析

简介: HashMap源码分析

HashMap简介:


  HashMap实现了Map接口,同时是基于哈希表的,允许存放null键和null值。HashMap和Hashtable相差无几,唯一的几点区别就是:HashMap是非线程安全的,而Hashtable是线程安全的(方法都用synchronized关键字进行修饰)。Hashtable不允许存放null键和null值。HashMap不能保证元素的存放顺序,是无序的。

当多个线程并发地访问HashMap时,就算只有一个线程修改了其结构(增加或删除键值对才算,改变键对应的值不算),必须对HashMap进行同步处理,否则会引发线程安全问题。


  如果没有同步对象,最好在开始之前使用Collections.synchronizedMap方法进行同步处理,防止非线程安全情况发生,示例如下:


Map m = Collections.synchronizedMap(new HashMap(…));


  当多个线程同时访问迭代器iterator时,若一个正在修改iterator,一个正在使用iteartor,将抛出ConcurrentModificationException。


HashMap定义:


public class HashMap<K,V> extends AbstractMap<K,V>

implements Map<K,V>, Cloneable, Serializable {


HashMap底层结构示意图(数组+链表结构,针对jdk1.7)


 

重要属性:


默认加载因子:

static final float DEFAULT_LOAD_FACTOR = 0.75f;

默认初始容量(必须是2的n次方,后面会解释):

static final int DEFAULT_INITIAL_CAPACITY = 16;

最大容量:

static final int MAXIMUM_CAPACITY = 1 << 30;

阈值(计算公式:threshold = capacity * load factor):

int threshold;


重要方法:(针对jdk1.7)


带参的构造函数:

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)//初始容量若小于0,则抛出非法的初始容量异常
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)//若初始容量超过最大容量,默认赋值最大容量
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))//加载因子若不大于0或者非浮点数,则抛出非法加载因子异常
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);//阈值计算
        table = new Entry[capacity];
        init();
    }


计算哈希值所在的数组索引:


该方法是为了计算哈希值对应的数组索引位置,本来是可以用h%length来实现的,但是&的效率要比%稍高一些,所以采用&来实现。


而只有length是2的n次方时,才满足h%length = h&(length-1),所以HahsMap的长度都是2的n次方倍,这个也解决了上面提到的容量为什么必须得2的n次方问题。


static int indexFor(int h, int length) {
        return h & (length-1);
    }

get方法(返回key对应的value):


public V get(Object key) {
        if (key == null)//key为null处理
            return getForNullKey();     //先计算key对应的哈希值,然后再对该值进行哈希操作,得到的结果用于查找key对应的table数组位置
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];//遍历key对应的table数组索引处的Entry链表
             e != null;
             e = e.next) {
            Object k;       //若key和某个Entry的key相同,则返回该Entry的value值
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;//无对应值,返回null
    }


put方法(添加键值对):


public V put(K key, V value) {
        if (key == null)//key是null处理
            return putForNullKey(value);
        int hash = hash(key.hashCode());//先计算key对应的哈希值,然后再对该值进行哈希操作,
        int i = indexFor(hash, table.length);//得到的结果用于计算对应键值对存放的位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {//遍历table数组该索引处的Entry链表
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//若存在key相同的,则用新值替换旧值
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;//修改计算+1
        addEntry(hash, key, value, i);//添加Entry
        return null;
    }


addEntry方法(将包含特定key、value、hash值的新Entry添加到特定的桶上):


void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        if (size++ >= threshold)//若键值对长度超过阈值,则将HashMap扩容2倍
            resize(2 * table.length);
    }


程序示例:


package com.abc;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
 * @date: 2018/09/19
 * @author: alan
 * 
 */
public class HashMapTest {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();//实例化HashMap
        map.put(1, "apple");//添加元素
        map.put(2, "banana");
        map.put(3, "watermelon");
        System.out.println("HashMap's size is : " + map.size());
        //foreach遍历map
        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
        for (Map.Entry<Integer, String> entry : entrySet) {
            int key = entry.getKey();
            String value = entry.getValue();
            System.out.println("key is " + key + ", value is " + value);
        }
        System.out.println("============================");
        //使用迭代器Iterator遍历map
        Iterator iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> entry = (Map.Entry)iterator.next();
            int key = entry.getKey();
            String value = entry.getValue();
            System.out.println("key is " + key + ", value is " + value);
        }
    }
}


控制台输出:


HashMap's size is : 3
key is 1, value is apple
key is 2, value is banana
key is 3, value is watermelon
============================
key is 1, value is apple
key is 2, value is banana
key is 3, value is watermelon
相关文章
|
6月前
|
Java
【JDK 源码分析】HashMap 操作方法
【1月更文挑战第27天】【JDK 源码分析】HashMap Put 元素 初始化
|
1月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
26 1
|
3月前
|
存储 缓存 安全
深度剖析Java HashMap:源码分析、线程安全与最佳实践
深度剖析Java HashMap:源码分析、线程安全与最佳实践
|
5月前
|
存储 Java 测试技术
滚雪球学Java(66):Java之HashMap详解:深入剖析其底层实现与源码分析
【6月更文挑战第20天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
45 3
滚雪球学Java(66):Java之HashMap详解:深入剖析其底层实现与源码分析
|
5月前
|
存储 算法 安全
JDK源码分析-HashMap
JDK源码分析-HashMap
|
6月前
|
存储 算法
HashMap源码分析
HashMap源码分析
|
6月前
|
安全 Java
【JDK 源码分析】HashMap 线程安全问题分析
【1月更文挑战第27天】【JDK 源码分析】HashMap 线程安全问题分析
|
6月前
|
存储 Java
【JDK 源码分析】HashMap 底层结构
【1月更文挑战第27天】【JDK 源码分析】HashMap 底层结构
|
6月前
|
存储 算法 Java
HashMap的源码分析(基于JDK1.8)
HashMap的源码分析(基于JDK1.8) Java中的HashMap是一种常用的数据结构,它是基于哈希表的数据结构,可以用来存储键值对。在HashMap中,每个键值对被称作一个Entry,每个Entry包含一个键和一个值。HashMap的实现基于数组和链表,数组用于存储Entry,链表用于解决哈希冲突。
|
存储 Java
[java进阶]——HashMap的底层实现原理和源码分析,另附几个高频面试题
[java进阶]——HashMap的底层实现原理和源码分析,另附几个高频面试题
170 0