Java的HashMap非常的常用,本篇研究它的实现算法,最后希望计算出内存占用,性能的量化数据,然后得出什么时候使用HashMap,什么时候不能滥用的结论。

HashMap实际上是一个数组,数组里面的每个元素都是一个链表。每个元素在通过put方法放入HashMap中的时候,要按照如下步骤进行:

1.根据该元素自身提供的hashcode计算出散列值,该散列值就是数组的下标。

2.将新元素放入该数组位置的链表中。

先来看一下数组的定义:

/**     
     * The table, resized as necessary. Length MUST Always be a power of two.   
     */

transient Entry[] table;
这是一个数组,transient关键字告诉我们它不会参与序列化。既然是一个数组,总有数目上限,也就意味着如果存入HashMap的元素太多,导致数组大小不能够存放所有的链表的时候,数组大小必须要能够调整。所以首先来考察一下数组容量的相关算法。

第一,Entry是什么类型?

static class Entry implements Map.Entry {

final K key;

V value;

Entry next;

final int hash;

/**    
         * Creates new entry.    
         */

Entry(int h, K k, V v, Entry n) {

value = v;

next = n;

key = k;

hash = h;

}

....

public final boolean equals(Object o) {

if (!(o instanceof Map.Entry))

return false;

Map.Entry e = (Map.Entry)o;

Object k1 = getKey();

Object k2 = e.getKey();

if (k1 == k2 || (k1 != null && k1.equals(k2))) {

Object v1 = getValue();

Object v2 = e.getValue();

if (v1 == v2 || (v1 != null && v1.equals(v2)))

return true;

}

return false;

}

public final int hashCode() {

return (key==null   ? 0 : key.hashCode()) ^

(value==null ? 0 : value.hashCode());

}

....
这是一个HashMap类的内部静态类。实现了Map.Entry接口。接受两个模板参数K和V。key和hash一旦在构造函数中被初始化,就不可改变,并且由于有next的存在,Entry可以构成一个单向链表。

比较重要的是equals和hashCode方法。代码先列出来,后面再解释。

第二,初始容量的设定

大多数都在下面的构造函数里面.用于指定的initialCapacity不准小于0,也不能超过最大值。并且最终的capicity必须是2的n次方。还有如果使用了无参数的构造函数,默认会创建一个拥有16个元素的数组。

public HashMap(int initialCapacity, float loadFactor) {

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal initial capacity: " +

initialCapacity);

if (initialCapacity > MAXIMUM_CAPACITY)

initialCapacity = MAXIMUM_CAPACITY;

if (loadFactor <= 0 || Float.isNaN(loadFactor))

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();

}
第三,什么时候应该调整数组的大小?

算法是这样,有一个变量size保存了实际数组已经使用了多少个元素,并且如果size的值达到了变量threshold的值,就必须扩充数组的容量。threshold=capicity*loadFactor.capicity是数组最大的容纳元素个数,loadFactor可以在构造函数中制定,否则采用默认值0.75f。capicity的最大值是1<<30(也就是2的30次方,1073741824).由此我们可以看到HashMap最多存放10亿多个链表。

第四,如何调整数组大小?

答案是2倍,很像C++里面的vector的分配策略。

void addEntry(int hash, K key, V value, int bucketIndex) {

Entry e = table[bucketIndex];

table[bucketIndex] = new Entry(hash, key, value, e);

if (size++ >= threshold)

resize(2 * table.length);

}

Java HashMap 分析四篇连载的更多相关文章

  1. 【JAVA并发第四篇】线程安全

    1.线程安全 多个线程对同一个共享变量进行读写操作时可能产生不可预见的结果,这就是线程安全问题. 线程安全的核心点就是共享变量,只有在共享变量的情况下才会有线程安全问题.这里说的共享变量,是指多个线程 ...

  2. Java学习第四篇:数组,排序,查找

    一.数组 1.一维数组 (1).数组的定义 数据类型 数组名[]=new 数据类型[大小] public class Demo1 { public static void main(String[] ...

  3. Java系列--第四篇 基于Maven的SSME之发送邮件

    在系列第一篇中,使用的是mybatis得到了一个小小的项目,而该项目的用户对象是有邮件地址的,如果按照邮件地址给对方去一封邮件会不会更能体现针对性呢,所以,我在这篇准备加入发送邮件的功能,利用的就是s ...

  4. 从.Net到Java学习第四篇——spring boot+redis

    从.Net到Java学习系列目录 “学习java已经十天,有时也怀念当初.net的经典,让这语言将你我相连,怀念你......”接上一篇,本篇使用到的框架redis.FastJSON. 环境准备 安装 ...

  5. Java【第四篇】基本语法之--循环

    循环语句功能 在循环条件满足的情况下,反复执行特定代码 循环语句的四个组成部分 初始化部分(init_statement)循环条件部分(test_exp) 循环体部分(body_statement) ...

  6. java核心技术第四篇之JDBC第二篇

    01.JDBC连接池_连接池的概念: 1).什么是连接池:对于多用户程序,为每个用户单独创建一个Connection,会使程序降低效率.这时我们可以创建一个"容器", 这个容器中, ...

  7. Java 学习 第四篇;面向对象(1)

    1:关于继承为了保证父类的良好封装性,不会被子类随意改变,设计父类时通常隐藏父类的内部数据,把父类属性改为private如果父类中可以被重写,但不希望被其他类自由访问可用protected修饰;2:什 ...

  8. 学习java随笔第四篇:运算符

    算术运算符 "+":加法运算符,也可做字符连接用途 "-":减法运算符 "*":乘法运算符 "/":除法运算符 &quo ...

  9. java基础第四篇之面向对象

    7.封装与面向对象 a.方法: public static void main(String[] args) { } 一般定义标准: 形参:一般把 不确定的量或者变化的量定义在形参位置//圆的的半径, ...

随机推荐

  1. 【转】如何学习android开发

    1.Java基础 很多朋友一上手就开始学习Android,似乎太着急了一些.Android应用程序开发是以Java语言为基础的,所以没有扎实的Java基础知识,只 是机械的照抄别人的代码,是没有任何意 ...

  2. vim编辑器基本操作介绍

    vim编辑器基本操作介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 可能很多小伙伴都听说过vi编辑器或是vim编辑器.它们是Unix和Linux世界最流行的编辑器之一,他们的特 ...

  3. mybatis+sqlserver中返回非自增主键

    首先把实体类贴出来(这里只贴出属性,其它的就是getter和setter方法): public class Around {     private String xccd;  //对应主键      ...

  4. pgadmin导出excel

    生成导入sql 第一行公式:="insert into province(code,name) values("&A2&",'"&B2& ...

  5. html5 canvas结构基础

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. JavaScript绝句的小研究

    前几日在网上看到一篇文章:JavaScript绝句,看了以后觉得里面的代码颇为有趣,不过文章里面只是简单的说了这样写的目的和结果,却没有令读者起到既知其然,又知其所以然的效果.这里简单写一篇小文章剖析 ...

  7. centos7.2 安装 composer

    安装Composer https://pkg.phpcomposer.com/#how-to-install-composer  # 下载composer.phar curl -sS https:// ...

  8. gcc __attribute__关键字举例之visibility【转】

    转自:https://blog.csdn.net/starstarstone/article/details/7493144?utm_source=tuicool&utm_medium=ref ...

  9. linux下查看各硬件型号

    查看主板型号 # dmidecode |grep -A 8 "System Information"System Information 上网查DELL CS24-TY,找到说主板 ...

  10. 常用SQL函数(字符串分隔转表、自增长转编号)

    字符串分隔转表 -- ============================================= -- Author: -- Create date: -- Description: ...