HashMap学习笔记

15人阅读 评论(1) 收藏 举报
分类:

HashMap学习笔记

  • 今天来学习面试以及平时工作学习中常用到的一个类HashMap。

概述:

  1. 什么是HashMap
  2. 常见操作(get和put)的工作原理
  3. Hash函数的实现
  4. Resize是什么

一、什么是HashMap

  • 官方描述:
    HashMap官方描述

    关键信息:基于Map、和HashTable类似但HashMap不能保证线程安全且允许键值对为null、不保证有序(个人觉得所谓的序是指“put”进来的顺序)、不保证顺序不随时间改变(个人认为是由于reSize的原因)

  • HashMap的结构:基于链表加数组

  • 值得一提的参数:

    • Initial Capacity 即table的大小,如果初始值大于允许的最大值N/0.75,那么reHash操作永远不会发生,默认值是16。

    • Load Factor:0.75,达到了良好的时间和空间之间的平衡开销的一个点。>0.75将会降低空间开销但会增加查找开销(在get和put操作中得以体现。

    • MIN_TREEIFY_CAPACITY:64,只有当able的大小达到64之后,链表转成树才可能实现,否则,当某节点的链长度大于8时只会发生table的resize操作。
    • UNTREEIFY_THRESHOLD:8,当某节点的链表长度大于该值时,发生链表到树的转换操作。
    • UNTREEIFY_THRESHOLD:6,当某节点的链表长度减少到该值时,发生树到链表的转换操作。
  • 值得一提的fail-fast:

    • 如果map在迭代器生成后被修改了(不包含迭代器自身的移除操作),迭代器将会抛出ConcurrentModificationException,当面对同步修改操作时,迭代器会快速失败。迭代器的快速失败原则应当尽量只在debug时被使用到。

二、HashMap的常见操作

  • put操作

    • 步骤

      1. 将key的hashCode()做hash运算,再计算index
      2. 碰撞了?链表长度小于等于8?链表形式放入buckets
      3. 碰撞了?链表长度大于8了?链表转成红黑树
      4. 没碰撞?直接放入buckets
      5. 节点存在了?替换!
      6. bucket大小大于阈值了?resize
    • 源码:
      public V put(K key, V value) {
      return putVal(hash(key), key, value, false, true);
      }

      final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
      boolean evict) {
      Node<K,V>[] tab; Node<K,V> p; int n, i;
      if ((tab = table) == null || (n = tab.length) == 0)
      n = (tab = resize()).length;
      if ((p = tab[i = (n - 1) & hash]) == null)
      tab[i] = newNode(hash, key, value, null);
      else {
      Node<K,V> e; K k;
      if (p.hash == hash &&
      ((k = p.key) == key || (key != null && key.equals(k))))
      e = p;
      else if (p instanceof TreeNode)
      e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
      else {
      for (int binCount = 0; ; ++binCount) {
      if ((e = p.next) == null) {
      p.next = newNode(hash, key, value, null);
      if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
      treeifyBin(tab, hash);
      break;
      }
      if (e.hash == hash &&
      ((k = e.key) == key || (key != null && key.equals(k))))
      break;
      p = e;
      }
      }
      if (e != null) { // existing mapping for key
      V oldValue = e.value;
      if (!onlyIfAbsent || oldValue == null)
      e.value = value;
      afterNodeAccess(e);
      return oldValue;
      }
      }
      ++modCount;
      if (++size > threshold)
      resize();
      afterNodeInsertion(evict);
      return null;
      }

  • get操作

    • 步骤:

      1. buckets中第一个节点的hash值是目标key的hash值:直接返回
      2. 有冲突时:是树吗?return ((TreeNode<K,V>)first).getTreeNode(hash, key);
      3. 不是树时:在链表中get
    • 代码:
      public V get(Object key) {
      Node<K,V> e;
      return (e = getNode(hash(key), key)) == null ? null : e.value;
      }

      final Node<K,V> getNode(int hash, Object key) {
      Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
      if ((tab = table) != null && (n = tab.length) > 0 &&
      (first = tab[(n - 1) & hash]) != null) {
      if (first.hash == hash && // always check first node
      ((k = first.key) == key || (key != null && key.equals(k))))
      return first;
      if ((e = first.next) != null) {
      if (first instanceof TreeNode)
      return ((TreeNode<K,V>)first).getTreeNode(hash, key);
      do {
      if (e.hash == hash &&
      ((k = e.key) == key || (key != null && key.equals(k))))
      return e;
      } while ((e = e.next) != null);
      }
      }
      return null;
      }

三、Hash函数的实现

  • 代码:
    static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  • 生动描述:

四、Resize()是什么

  • 当进行put操作时,当前所占bucket的程度已达到了 LoadFactor*Capacity时,就会触发resize操作,也就是把bucket的容量扩大两倍,然后重新计算各个节点的index值重新分配到新的bucket中。注释如下:

  • Resize的实现


    当hash1新增的比特位(右边开始第五个)为0的时候,则index不变,如果是1的话,则为原位置加oldCapacity。这样就不需要经过复杂的index计算,是不是很精妙呢?

查看评论

hashmap 实例

  • 2011年08月30日 13:10
  • 23KB
  • 下载

Kotlin学习笔记(二)——示例篇三

该篇继续根据Kotlin语言的Demo,来学习Kotlin的使用!本文继上一篇文章来继续学习的,如果有些不懂,请参考上一篇文章 参考链接地址:http://try.kotlinlang.org/#/...
  • zouchengxufei
  • zouchengxufei
  • 2016-01-21 13:42:48
  • 4579

HashMap源码分析

  • 2013年03月11日 16:44
  • 359KB
  • 下载

Android码农秘籍-01-HashMap详解

带你阅读HashMap源码,搞懂HashMap的细枝末节
  • 2018年03月08日 09:54

hashMap存储分析

  • 2013年02月26日 15:48
  • 3KB
  • 下载

HASHMAP缓存.txt

  • 2010年09月01日 13:38
  • 2KB
  • 下载

HashMap排序

  • 2012年06月04日 23:52
  • 3KB
  • 下载

HashMap的学习笔记

HashMap的存储结构可以理解成数组+链表的结构,但是也不完全这样理解,因为数组查找元素的时候,需要进行遍历,严格来说,数组是一种顺序查找,当数组进行查找的时候,第一个元素找到的时间肯定比最后一个元...
  • qq_34523020
  • qq_34523020
  • 2017-08-25 10:12:09
  • 89

词法分析器java

  • 2011年11月13日 18:47
  • 48KB
  • 下载

细说java.util.HashMap

HashMap是我们最常用的类之一,它实现了hash算法,虽然使用很简单,但是其实现有很多值得研究的地方。 HashMap存储的是key-value形式的键值对,这个键值对在实现中使用一个静态内部类...
  • mhmyqn
  • mhmyqn
  • 2015-09-01 00:02:24
  • 5176
    个人资料
    持之以恒
    等级:
    访问量: 1626
    积分: 214
    排名: 34万+
    最新评论