HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是由于它是通过计算散列码来决定存储的位置。

HashMap中主要是通过key的hashCode来计算hash值的。仅仅要hashCode同样。计算出来的hash值就一样。假设存储的对象对多了,就有可能不同的对象所算出来的hash值是同样的,这就出现了所谓的hash冲突。

学过数据结构的同学都知道。解决hash冲突的方法有非常多,HashMap底层是通过链表来解决hash冲突的。

    HashMap事实上就是一个Entry数组。Entry对象中包括了键和值,当中next也是一个Entry对象。它就是用来处理hash冲突的。形成一个链表。数组的每一个元素都是一个单链表的头节点,链表是用来解决冲突的。假设不同的key映射到了数组的同一位置处,就将其放入单链表中。


 Hashtable 与 HashMap类似,可是主要有6点不同。

 1.HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个差别就像Vector和ArrayList一样。  
 2.HashTable不同意null值,key和value都不能够,HashMap同意null值,key和value都能够。HashMap同意key值仅仅能由一个null值,由于hashmap假设key值同样。新的key,
value将替代旧的。  
 3.HashTable有一个contains(Object value)功能和containsValue(Object value)功能一样。  
 4.HashTable使用Enumeration,HashMap使用Iterator。

 5.HashTable中hash数组默认大小是11,添加的方式是 old*2+1。HashMap中hash数组的默认大小是16,并且一定是2的指数。

1、HashTable的方法是同步的,HashMap未经同步
    Hashtable 提供的几个主要方法,包含 get(), put(), remove() 等。每一个方法本身都是 synchronized 的,会对整个对象进行加锁操作,不会出现两个线程同一时候对数据进行操作的情况。因此保证了线程安全性,可是也大大的减少了运行效率。

2、HashMap中hash数组的默认大小是16,并且一定是2的指数
  1. static int indexFor(int h, int length) {
  2. return h & (length-1);
  3. }
    将key的二次hash值。与长度减一进行与操作,这一步可谓经典,通常我们会用取模的方式来定位数组中的某个位置,我们首先想到的就是把hashcode对数组长度取模运算,这样一来,元素的分布相对来说是比較均匀的。

可是,“模”运算的消耗还是比較大的。能不能找一种更高速,消耗更小的方式那?hashMap用这样的方法,并且length即capacity的值。面capacity又是2的倍数,减1之后。表示成二进制就所有是1了。那么与所有为1的一个数进行与操作,速度会大大提升了。这就是为什么"capacity的值是2的倍数"


3、HashMap解决冲突的办法
    1)再哈希法
    2)拉链发
  1. public V put(K key, V value) {
  2. if (key == null)
  3. return putForNullKey(value);
  4. int hash = hash(key.hashCode());
  5. int i = indexFor(hash, table.length);
  6. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  7. Object k;
  8. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  9. V oldValue = e.value;
  10. e.value = value;
  11. e.recordAccess(this);
  12. return oldValue;
  13. }
  14. }
  15. modCount++;
  16. addEntry(hash, key, value, i);
  17. return null;
  18. }
    依据key的hash值得到这个元素在数组中的位置(即下标),然后就能够把这个元素放到相应的位置中了。假设这个元素所在的位子上已经存放有其它元素了,那么在同一个位子上的元素将以链表的形式存放,新增加的放在链头。最先增加的放在链尾。从hashmap中get元素时。首先计算key的hashcode,找到数组中相应位置的某一元素,然后通过key的equals方法在相应位置的链表中找到须要的元素。

    调用int hash = hash(key.hashCode());这是hashmap的一个自己定义的hash,在key.hashCode()基础上进行二次hash。再哈希法可以解决开放地址法的聚集现象,可是却添加了时间复杂度。
  1. static int hash(int h) {
  2. h ^= (h >>> 20) ^ (h >>> 12);
  3. return h ^ (h >>> 7) ^ (h >>> 4);
  4. }
    改进传统的hash方法,并且尽量保证key的每一位都会影响到最后的hash值。以达到降低hash冲突的目的.

4、HashMap的构造函数
  1. if (initialCapacity < 0)
  2. throw new IllegalArgumentException("Illegal initial capacity: " +
  3. initialCapacity);
  4. if (initialCapacity > MAXIMUM_CAPACITY)
  5. initialCapacity = MAXIMUM_CAPACITY;
  6. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  7. throw new IllegalArgumentException("Illegal load factor: " +
  8. loadFactor);
  9. // Find a power of 2 >= initialCapacity
  10. int capacity = 1;
  11. while (capacity < initialCapacity)
  12. capacity <<= 1;
  13. this.loadFactor = loadFactor;
  14. threshold = (int)(capacity * loadFactor);
  15. table = new Entry[capacity];
  16. init();
  17. }
    loadFactor :载入因子,载入因子与HashMap resize有关。

默觉得0.75

    capacity:容器大小。默认值为16 其大小为上面所说的数据结构中数组的长度。

    table:即为上面数据结构图中。X方向的数组(transient Entry[] table;)
    threshold :resize的临界值,即当HashMap中无素个数达到该值时,HashMap就会调用其resize方法。又一次扩充大小。
    while (capacity < initialCapacity)
              capacity <<= 1;  
    这段代码说明什么:capacity的值是2的倍数,即使设置初始值是1000,hashmap也自己主动会将其设置为1024。

5、哈希表解决冲突的经常用法
    1)开放地址法
    2)拉链法
    3)再哈希法

拉链法的长处 ,与开放定址法相比,拉链法有例如以下几个长处: 
①拉链法处理冲突简单。且无堆积现象。即非同义词决不会发生冲突。因此平均查找长度较短。
②因为拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
③开放定址法为降低冲突,要求装填因子α较小。故当结点规模较大时会浪费非常多空间。

而拉链法中可取α≥1,且结点较大时,拉链法中添加的指针域可忽略不计,因此节省空间;

④在用拉链法构造的散列表中,删除结点的操作易于实现。仅仅要简单地删去链表上对应的结点就可以。而对开放地址法构造的散列表,删除结点不能简单地将被删结 点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。这是由于各种开放地址法中,空地址单元(即开放地址)都是查找失败的条件。因此在
用开放地址法处理冲突的散列表上运行删除操作,仅仅能在被删结点上做删除标记。而不能真正删除结点。
拉链法的缺点 
拉链法的缺点是:指针须要额外的空间。故当结点规模较小时,开放定址法较为节省空间,而若将节省的指

HashMap源代码学习笔记的更多相关文章

  1. [Java] Map / HashMap - 源代码学习笔记

    Map 1. 用于关联 key 和 value 的对象,其中 key 与 key 之间不能重复. 2. 是一个接口,用来代替 Java 早期版本中的 Dictionary 抽象类. 3. 提供三种不同 ...

  2. [Java] LinkedList / Queue - 源代码学习笔记

    简单地画了下 LinkedList 的继承关系,如下图.只是画了关注的部分,并不是完整的关系图.本博文涉及的是 Queue, Deque, LinkedList 的源代码阅读笔记.关于 List 接口 ...

  3. jQuery源代码学习笔记_工具函数_noop/error/now/trim

    jQuery源代码学习笔记_工具函数_noop/error/now/trim jquery提供了一系列的工具函数,用于支持其运行,今天主要分析noop/error/now/trim这4个函数: 1.n ...

  4. [Java] List / ArrayList - 源代码学习笔记

    在阅读 List / ArrayList 源代码过程中,做了下面的笔记. LinkedList 的笔记较多,放到了另一篇博文 LinkedList / Queue- 源代码学习笔记 List List ...

  5. Java Jdk1.8 HashMap源代码阅读笔记二

    三.源代码阅读 3.元素包括containsKey(Object key) /** * Returns <tt>true</tt> if this map contains a ...

  6. jQuery源代码学习笔记:jQuery.fn.init(selector,context,rootjQuery)代码具体解释

    3.1 源代码 init: function( selector, context, rootjQuery ) { var match, elem, ret, doc; // Handle $(&qu ...

  7. jQuery源代码学习笔记:构造jQuery对象

    2.1源代码结构: (function( window, undefined ) { var jQuery = (function() { // 构建jQuery对象 var jQuery = fun ...

  8. [Java] TreeMap - 源代码学习笔记

    TreeMap 实现了 SortedMap 和 NavigableMap 接口,所有本文还会记录 SortedMap 和 NavigableMap 的阅读笔记. SortedMap 1. 排序的比较应 ...

  9. Angular源代码学习笔记-原创

    时间:2014年12月15日 14:15:10 /** * @license AngularJS v1.3.0-beta.15 * (c) 2010-2014 Google, Inc. http:// ...

随机推荐

  1. 关于requirejs和grunt压缩合并是否矛盾

    requirejs主要是为了模块化开发,这样带来的好处不言而喻.但是分成多个js文件增加了请求数,那么就要用到合并压缩.合并压缩了原来的许多独立的js模块,那requirejs又是怎么冲压缩的文件中找 ...

  2. 集群高可用之lvs+keepalive

    集群高可用之lvs+keepalive keepalive简介: 负载均衡架构依赖于知名的IPVS内核模块,keepalive由一组检查器根据服务器的健康情况动态维护和管理服务器池.keepalive ...

  3. zabbix的安装(一)监控os资源:内存,cpu,io,负载,带宽

    一.Linux下开源监控系统简单介绍1)cacti:存储数据能力强,报警性能差2)nagios:报警性能差,存储数据仅有简单的一段可以判断是否在合理范围内的数据长度,储存在内存中.比如,连续采样数据存 ...

  4. vscode & code snippets

    code snippets vscode & code snippets https://github.com/xgqfrms/FEIQA/tree/master/000-xyz/templa ...

  5. DIV垂直/水平居中2(DIV宽度和高度是动态的)

    <!doctype html><html><head><meta charset="utf-8"><title>块元素D ...

  6. 【Luogu】P2158仪仗队(欧拉函数)

    题目链接 首先来介绍欧拉函数. 设欧拉函数为f(n),则f(n)=1~n中与n互质的数的个数. 欧拉函数有三条引论: 1.若n为素数,则f(n)=n-1; 2.若n为pa,则f(n)=(p-1)*(p ...

  7. 解开Future的神秘面纱之任务执行

    此文承接之前的博文 解开Future的神秘面纱之取消任务 补充一些任务执行的一些细节,并从全局介绍程序的运行情况. 任务提交到执行的流程 前文我们已经了解到一些Future的实现细节,这里我们来梳理一 ...

  8. bzoj 2017 [Usaco2009 Nov]硬币游戏 动态规划

    [Usaco2009 Nov]硬币游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 431  Solved: 240[Submit][Status] ...

  9. EC++学习笔记(一) 习惯c++

    条款01:c++多范式编程语言 条款02:尽量以 const, enum, inline 替换#define 1. 提供类型安全检查 2. 去除函数调用开销 3. 避免宏的二义性 const doub ...

  10. 洛谷 [P2953] 牛的数字游戏

    SG搜索 n的范围在可以接受的范围内,SG搜索即可 #include <iostream> #include <cstdio> #include <cstring> ...