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. 关于Linux下使用expdp和impdp命令对Oracle数据库进行导入和导出操作

    说明:本次导入和导出采用expdp和impdp命令进行操作,这2个命令均需要在服务器端进行操作 http://www.cnblogs.com/huacw/p/3888807.html 一.    从O ...

  2. HTML与XML的区别

    什么是HTML HTML的全拼是Hypertext Markup Language, 中文也就是超文本链接标示语言.HTML(HyperTextMark-upLanguage)即超文本标记语言,是WW ...

  3. LOGMNR分析redo log和archive log教程

    自Oracle 11g起,无需设置UTL_FILE_DIR就可以使用LOGMNR对本地数据库的日志进行分析,以下是使用LOGMNR的DICT_FROM_ONLINE_CATALOG分析REDO和归档日 ...

  4. [LoadRunner]LR性能测试结果样例分析

    R性能测试结果样例分析 测试结果分析 LoadRunner性能测试结果分析是个复杂的过程,通常可以从结果摘要.并发数.平均事务响应时间.每秒点击数.业务成功率.系统资源.网页细分图.Web服务器资源. ...

  5. 【PL/SQL编程基础】

    [PL/SQL编程基础]语法: declare 声明部分,例如定义变量.常量.游标 begin 程序编写,SQL语句 exception 处理异常 end: / 正斜杠表示执行程序快范例 -- Cre ...

  6. Centos7 编译安装python3

    step1:preparation $ yum install yum-utils make wget gcc $yum-builddep python step2:download $ wget h ...

  7. [android开发篇]自定义权限

    有时候,我们可能遇到如下需求场景:当用户在一个应用程序中进行某项操作时,会启动另外一个应用程序,最常见的时直接打开了另外一个应用程序,并进入其中某个Activity(如:有的应用中有推荐应用列表,当用 ...

  8. Git升级迁移

    1 原有机器进行升级和备份: https://about.gitlab.com/update/#centos-6 1. Make a backup (Optional) If you would li ...

  9. 如何将一个int转换成cstring

    如:int a = 5;CString b;b.Format("%d",a);补充:如果a是double,或a是float的就是:b.Format("%f",a ...

  10. java.util.ResourceBundle 用法小介

    java中读取配置文件的信息可以采用properties这个类,但是当遇到国际化问题的时候还是不好解决,因而还是最好使用 ResourceBundle这个类,其实ResourceBundle本质上和P ...