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

  

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

既然是线性数组,为什么能随机存取?这里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. 基于嵌入式操作系统VxWorks的多任务并发程序设计(1)――基本概念

    1引言 嵌入式系统定义义为:嵌入到对象体系中的专用计算机系统."嵌入性"."专用性"与"计算机系统"是嵌入式统的三个基本要素,对象系统则是指 ...

  2. VTK显示.vtk格式文件

    void ReadandShowVTKFile () { vtkSmartPointer<vtkRenderer > aRenderer = vtkSmartPointer<vtkR ...

  3. Windows7 64位安装最新版本MySQL服务器

    Windows7 64位安装最新版本MySQL服务器 近期,一直在研究MySQL数据库,经常修改配置文件,导致MySQL数据库无法使用,不得不反复重装MySQL数据库.以下是在Windows7 64位 ...

  4. TypeError: Error #1034: 强制转换类型失败:无法将 mx.controls::DataGrid@9a7c0a1 转换为 spark.core.IViewport。

    1.错误描述 TypeError: Error #1034: 强制转换类型失败:无法将 mx.controls::DataGrid@9aa90a1 转换为 spark.core.IViewport. ...

  5. SDL 在指定窗口中绘图

    SDL默认会自动创建绘图窗口,可以通过设置环境变量,让其在指定窗口绘图.代码如下: [cpp] view plaincopyprint? char sdl_var[64]; sprintf(sdl_v ...

  6. javaWeb之邮箱发送(邮箱客户端配置)

    这里使用的是本机的邮箱服务器  , 代码执行条件: 1.·邮箱服务器  , 下载地址   密码   s4xn 邮箱服务器配置: 1):安装 2):打开服务器 红色部分是默认账号,不用处理 3)系统设置 ...

  7. Tornado模块

    Tornado 一个轻量级的Web框架 简介 1.Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的框架.此外,它还拥有处理安全性.用户验证.社 ...

  8. C#的动态链接库和XML配置

    今天对昨天的实例进行了改进和提高,将堆排序和冒泡排序封装在一个动态链接库中,提供函数调用和事件委托.此外加入XML进行配置,在XML中存入相关配置信息,提供一个单独的XmlClass对其进行操作,加深 ...

  9. freemarker中的round、floor和ceiling数字的舍入处理(十七)

    1.简易说明 (1)round:四舍五入 (2)floor:向下取整 (3)ceiling:向上取整 2.举例说明 <#--freemarker中的round.floor和ceiling数字的舍 ...

  10. WPF基础篇之控件模板(ControlTemplate)

    WPF中每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应.我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件. 与Style不同,Style只能改变控件 ...