Map--HashMap实现分析

原创 2015年07月08日 23:34:30

HashMap:

利用数组和链表的数据结构存储<key,value>

链表:

static class Node<K,V> implements Map.Entry<K,V>
数组:

transient Node<K,V>[] table;
储存过程:

1对key进行哈希

2根据哈希值找到数组中对应位置

3如果该位置没有链表则作为头部放入,如果该位置存在链表则遍历链表,如果没找到则在尾部加入<key,value>

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);//链表中没找到在尾部新增<key,value>
                        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//更新<key,value>
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
读取过程:

1对key进行哈希

2根据哈希值找到数组中对应位置的头部节点

3判断key是否与头部以及链表其他节点相同

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;
    }

resize:

HashMap设置了三个参数:

threshold当前允许元素最大数

loadFactor负载因子

capacity数组大小

loadFactor=threshold/capacity

如果loadFactor太小则浪费空间,太大在每次put和get的时候线性时间会比较长,因此一般固定loadFactor,默认为0.75,当元素数量达到threshold则进行resize扩大capacity以及threshold

resize步骤:

1如果table为空采用默认的capacity和threshold,如果不为空则扩大一倍

2将原来table中的<key,value>放入新的table

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;//放入新的table
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
hash技巧:

由于数组长度有限,如果仅仅利用hashcode和数组长度n-1与操作,那么高位不同低位相同将会哈希到同一个桶中,增加了碰撞率。因此采用高位计算,将hashcode右移把高位和低位进行异或。

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
同时在计算数组中的对应位置时,与数组长度n-1进行与操作,而数组长度都是2的指数次,这样使n-1的二进制码为全1,就可以避免0到n-1之间不同的值与操作完被分配到相同的位置。下面的函数保证了数组长度为2的指数次:

static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }





Map--HashMap

HashMap为Map的实现类,无参构造方法调用参数为int(初始容量)和float(加载因子)的构造方法初始化变量.HashMap的内部存储结构其实是数组和链表的结合,数据内容为键值对(key-va...
  • a3781386
  • a3781386
  • 2016年05月07日 14:15
  • 259

JAVA语言实现编译原理的LR分析过程模拟

通过JAVA实现SLR1的分析过程
  • TAB_YLS
  • TAB_YLS
  • 2017年05月04日 18:58
  • 783

LL(1)分析法_C++实现

Code by C++#include #include #include /*******************************************/int count=0; ...
  • shaguabufadai
  • shaguabufadai
  • 2017年05月28日 23:41
  • 382

Anbox 实现分析之程序入口

Anbox 的总体架构如 运行 Anbox 一文的相关内容所述,其运行时主要由两个分开的实例构成,容器管理器和会话管理器。anbox 用同一个可执行文件,在启动时通过不同的参数实现运行时执行两块完全不...
  • tq08g2z
  • tq08g2z
  • 2018年01月09日 17:29
  • 65

煮酒谈报表展示(即席分析,模板分析)

用过水晶报表、FastReport或某些国产报表组件的人,都知道每一张展示得很漂亮的报表,必须经过事先的报表定义过程。一般的报表软件会提供一个报表设计器(要代码实现报表多怕怕呀),很多厂家的买点就是说...
  • sanlink2000
  • sanlink2000
  • 2006年04月24日 14:23
  • 2380

如何用BI实现购物篮分析

如今的零售卖场,商品跟别人差不多、价格跟别人差不多、布局也跟别人差不多,基本什么都差不多甚至有些方面还不如人的时候,如何去寻找突破?有一个至关重要但却被国内大多数零售企业忽略了的因素——那就是“顾客”...
  • love__sz
  • love__sz
  • 2011年07月07日 09:48
  • 2043

Anbox 实现分析之 I/O 模型

Anbox 运行时主要由两个分开的实例构成,即容器管理器 ContainerManager 和会话管理器 SessionManager,但无论是 ContainerManager 还是 Session...
  • tq08g2z
  • tq08g2z
  • 2018年01月11日 19:37
  • 60

数据结构与算法分析笔记与总结(java实现)--数组10:数组中只出现一次的数字

数据结构与算法分析笔记与总结(java实现)--数组10:数组中只出现一次的数字
  • qq_27703417
  • qq_27703417
  • 2017年02月15日 16:47
  • 143

程序性能的初步优化与分析(以 C++ 为例)

去年圣诞节浏览了 Milo Yip 的文章 “如何用 C 语言画一棵圣诞树” 后,对这个圣诞树生成算法很感兴趣。 下面对这个程序,做一些简单的优化分析......
  • andytimes
  • andytimes
  • 2016年06月09日 02:59
  • 670

比较好的页面效果线性图表

http://www.oesmith.co.uk/morris.js/
  • myrainblues
  • myrainblues
  • 2014年03月27日 14:40
  • 440
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Map--HashMap实现分析
举报原因:
原因补充:

(最多只允许输入30个字)