简单说: 底层原理就是采用数组加链表:

  

两张图片很清晰地表明存储结构:

既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现:

// 存储时: 
int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值 
int index = hash % Entry[].length; 
Entry[index] = value;

// 取值时: 
int hash = key.hashCode(); 
int index = hash % Entry[].length; 
return Entry[index];

public V put(K key, V value) {
 
        if (key == null)
 
            return putForNullKey(value); //null总是放在数组的第一个链表中
 
        int hash = hash(key.hashCode());
 
        int i = indexFor(hash, table.length);
 
        //遍历链表
 
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
 
            Object k;
 
            //如果key在链表中已存在,则替换为新value
 
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
 
                V oldValue = e.value;
 
                e.value = value;
 
                e.recordAccess(this);
 
                return oldValue;
 
            }
 
        }
 
 
        modCount++;
 
        addEntry(hash, key, value, i);
 
        return null;
 
    }
 
 
 
 
void addEntry(int hash, K key, V value, int bucketIndex) {
 
    Entry<K,V> e = table[bucketIndex];
 
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //参数e, 是Entry.next
 
    //如果size超过threshold,则扩充table大小。再散列
 
    if (size++ >= threshold)
 
            resize(2 * table.length);
 
}
get()
 
public V get(Object key) {
 
        if (key == null)
 
            return getForNullKey();
 
        int hash = hash(key.hashCode());
 
        //先定位到数组元素,再遍历该元素处的链表
 
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
 
            e != null;
 
            e = e.next) {
 
            Object k;
 
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
 
                return e.value;
 
        }
 
        return null;
 
}
 
 
 
null key的存取
 
null key总是存放在Entry[]数组的第一个元素。
 
 
  private V putForNullKey(V value) {
 
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
 
            if (e.key == null) {
 
                V oldValue = e.value;
 
                e.value = value;
 
                e.recordAccess(this);
 
                return oldValue;
 
            }
 
        }
 
        modCount++;
 
        addEntry(0, null, value, 0);
 
        return null;
 
    }
 
 
    private V getForNullKey() {
 
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
 
            if (e.key == null)
 
                return e.value;
 
        }
 
        return null;
 
    }
 
再散列rehash过程
 
当哈希表的容量超过默认容量时,必须调整table的大小。当容量已经达到最大可能值时,那么该方法就将容量调整到Integer.MAX_VALUE返回,这时,需要创建一张新表,将原表的映射到新表中。
 
 
  /**
 
    * Rehashes the contents of this map into a new array with a
 
    * larger capacity.  This method is called automatically when the
 
    * number of keys in this map reaches its threshold.
 
    *
 
    * If current capacity is MAXIMUM_CAPACITY, this method does not
 
    * resize the map, but sets threshold to Integer.MAX_VALUE.
 
    * This has the effect of preventing future calls.
 
    *
 
    * @param newCapacity the new capacity, MUST be a power of two;
 
    *        must be greater than current capacity unless current
 
    *        capacity is MAXIMUM_CAPACITY (in which case value
 
    *        is irrelevant).
 
    */
 
    void resize(int newCapacity) {
 
        Entry[] oldTable = table;
 
        int oldCapacity = oldTable.length;
 
        if (oldCapacity == MAXIMUM_CAPACITY) {
 
            threshold = Integer.MAX_VALUE;
 
            return;
 
        }
 
 
        Entry[] newTable = new Entry[newCapacity];
 
        transfer(newTable);
 
        table = newTable;
 
        threshold = (int)(newCapacity * loadFactor);
 
    }
 
 
 
 
    /**
 
    * Transfers all entries from current table to newTable.
 
    */
 
    void transfer(Entry[] newTable) {
 
        Entry[] src = table;
 
        int newCapacity = newTable.length;
 
        for (int j = 0; j < src.length; j++) {
 
            Entry<K,V> e = src[j];
 
            if (e != null) {
 
                src[j] = null;
 
                do {
 
                    Entry<K,V> next = e.next;
 
                    //重新计算index
 
                    int i = indexFor(e.hash, newCapacity);
 
                    e.next = newTable[i];
 
                    newTable[i] = e;
 
                    e = next;
 
                } while (e != null);
 
            }
 
        }
    }

HashMap的底层原理的更多相关文章

  1. HashMap的底层原理(jdk1.7.0_79)

    前言 在Java中我们最常用的集合类毫无疑问就是Map,其中HashMap作为Map最重要的实现类在我们代码中出现的评率也是很高的. 我们对HashMap最常用的操作就是put和get了,那么你知道它 ...

  2. 谈一下HashMap的底层原理是什么?

    底层原理:Map + 无序 + 键唯一 + 哈希表 (数组+Entry)+ 存取值 1.HashMap是Map接口的实现类.实现HashMap对数据的操作,允许有一个null键,多个null值. Co ...

  3. HashMap的底层原理 cr:csdn:zhangshixi

    1.    HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变 ...

  4. 深度解析HashMap集合底层原理

    目录 前置知识 ==和equals的区别 为什么要重写equals和HashCode 时间复杂度 (不带符号右移) >>> ^异或运算 &(与运算) 位移操作:1<&l ...

  5. 浅谈HashMap 的底层原理

    本文整理自漫画:什么是HashMap? -小灰的文章 .已获得作者授权. HashMap 是一个用于存储Key-Value 键值对的集合,每一个键值对也叫做Entry.这些个Entry 分散存储在一个 ...

  6. HashMap 的底层原理

    1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1 ...

  7. 最简单的HashMap底层原理介绍

    HashMap 底层原理  1.HashMap底层概述 2.JDK1.7实现方式 3.JDK1.8实现方式 4.关键名词 5.相关问题 1.HashMap底层概述 在JDK1.7中HashMap采用的 ...

  8. HashMap的底层实现原理

    HashMap的底层实现原理1,属性static final int MAX_CAPACITY = 1 << 30;//1073741824(十进制)0100000000000000000 ...

  9. HashMap底层原理分析(put、get方法)

    1.HashMap底层原理分析(put.get方法) HashMap底层是通过数组加链表的结构来实现的.HashMap通过计算key的hashCode来计算hash值,只要hashCode一样,那ha ...

随机推荐

  1. Procedure execution failed 2013 - Lost connection to MySQL server during query

    1 错误描述 Procedure execution failed 2013 - Lost connection to MySQL server during query 2 错误原因 由错误描述可知 ...

  2. JSP中的编译指令和动作指令的区别

    JSP中的编译指令和动作指令的区别 1.编译指令是通知Servlet引擎的处理消息,而动作指令只是运行时的脚本动作 2.编译指令是在将JSP编译成Servlet时起作用,而动作指令可替换成JSP脚本, ...

  3. OpenStack_I版 3.glance部署

    存储镜像path                 默认镜像不存储在本地,一般放在swift对象存储或Cinder块存储里   glance安装     拷贝配置文件到/ect下,并新建配置目录,日志目 ...

  4. 堡垒机-teleport的安装以及常见问题解决办法

    teleport是一款简单易用的堡垒机系统,运用在企业对windows.linux服务器的安全使用管理以及审计. 官网网址:http://teleport.eomsoft.net/ github地址: ...

  5. Codeforces Round #427 (Div. 2) D - Palindromic characteristics

    本题是个简单的区间dp 最近都没时间做题了,被我妈强制喊回去,然后颓废了10天(回家也没发控制住自己= = 我的锅),计划都打乱了,本来还报名了百度之星,然后没时间参加 #include<cma ...

  6. C#图解教程 第十章 结构

    结构 什么是结构结构是值类型对结构赋值构造函数和析构函数 实例构造函数静态构造函数构造函数和析构函数小结 字段初始化语句是不允许的结构是密封的装箱和拆箱结构作为返回值和参数 关于结构的其他信息 结构 ...

  7. [UVAlive4297]First Knight

    题面在这里 题意 给定一个\(n\times m\)的格网,从\((1,1)\)出发,每一格\((i,j)\)往上下左右移动的概率已经给出,询问到达\((n,m)\)的期望步数 数据范围 \[n,m\ ...

  8. Bzoj4916: 神犇和蒟蒻

    题面 传送门 Sol 第一问puts("1") 第二问,\(\varphi(i^2)=i\varphi(i)\) 设\(\phi(n)=\sum_{i=1}^{n}i\varphi ...

  9. [BZOJ1543] 生成树计数 (Kruskal)

    Description 给定一个连通的带边权的图(允许自环和重边),求不同的最小生成树个数.两个生成树不同当它们所用的边的序号不同,换句话说,重边算多次. Input 第一行n,m,表示点数和边数(1 ...

  10. LightOJ1259 Goldbach`s Conjecture

    题面 T组询问,每组询问是一个偶数n 验证哥德巴赫猜想 回答n=a+b 且a,b(a<=b)是质数的方案个数 Input Input starts with an integer T (≤ 30 ...