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

  

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

既然是线性数组,为什么能随机存取?这里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. FusionCharts饼图中label值太长怎么解决

    FusionCharts饼图中label值太长怎么解决 1.使用hoverText属性 <?xml version="1.0" encoding="UTF-8&qu ...

  2. linux虚拟机中安装mongodb

    今天在linux虚拟机中安装了mongodb数据库,安装过程主要参照了网上的一片文档,大部分地方都没有多大区别,只是在某些细节上有所改变. 我参考的文档是:http://my.oschina.net/ ...

  3. Django学习-21-表关系参数

    一对多关系 ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要关联的表中 ...

  4. WPF基础篇之移动特效

    前一段时间,在做动画特效的时候,在网上看到了一个水平移动控件的例子.里面用到了RenderTransform特效.在网上查找资料发现了一篇基础的文章: 文章源地址:http://www.ithao12 ...

  5. SharedPreferences实现保存用户名功能

    1. 简介   SharedPreferences是一种轻型的数据存储方式,通过key-value键值对的方式将数据存储在xml文件中,常用于存储简单的配置信息. 2. 使用方式 2.1 获取Shar ...

  6. GridView添加事件监听和常用属性解析

    1. 使用流程 graph LR 准备数据源-->新建适配器 新建适配器-->绑定数据源 绑定数据源-->加载适配器 2. 常用属性 android:columnWidth:每一列的 ...

  7. jtyhon 介绍

    Jython是Python的纯Java实现.她无缝地结合了Java类与Python,使用户能以Python语言的语法编写在Java虚拟机上运行的 软件.它的特点有:与相似的Java程序相比,Jytho ...

  8. Java解析YAML和Android解析YAML

    一.Java解析YAML 1. API的选择 一般分两种:Jyaml和snakeYAML.(Jyaml下载地址:http://download.csdn.net/detail/dgssfgfs/847 ...

  9. centos svn 服务器间的数据迁移

    svnadmin dump erp > ~/erp.svn   当前目录下的erp 导出到根目录下名为erp.svn tar -zcvf backupSvn.tar.gz backupSvn   ...

  10. 【Luogu1345】奶牛的电信(网络流)

    [Luogu1345]奶牛的电信(网络流) 题面 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成 ...