码迷,mamicode.com
首页 > 其他好文 > 详细

浅谈HashMap

时间:2020-01-13 23:27:58      阅读:95      评论:0      收藏:0      [点我收藏+]

标签:lse   ini   不同的   时间   toc   出错   public   equal   安全   

浅谈HashMap

基本特性

定义

hashMap是一个无序的,非空的容器,而且是非同步的容器会造成线程不安全的这类问题,即有许多人都想要同一份学习资料,系统会复制出多份资料后,给每个人一份资料,而不同的人对这份资料有着不同的看法并对该资料进行修改,再上传到系统中。可想而知资料会有多少个版本,但是系统只能存放一个版本的资料,因而会丢失大量版本信息。线程不安全:简单来说,就是用户读到的信息有一定可能是错误的,从而做出错误的操作(抢票时,可能抢到重票或抢到一张不存在的票)

技术图片

历遍

HashMap的容量太大或太小,不利于literation(迭代器)查询目标。

技术图片

HashMap的模型

HashMap是数组和单向列表的结合体,即用数组来装列表的表头,因此在做增删等操作时,所消耗的时间和空间会比数组小,查询容器的中元素的速度会比列表快。类似于下图

技术图片

初始化

HashMash中常量:

技术图片

技术图片

HashMap有4个构造函数;
public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; //使用默认的加速因子,bucket 的大小为默认的16
    }
 1  public HashMap(int initialCapacity, float loadFactor) {
 2 //initialCapacity是bucket的大小,loadFactor是加速因子
 3         if (initialCapacity < 0)
 4             throw new IllegalArgumentException("Illegal initial capacity: " +
 5                                                initialCapacity);
 6         if (initialCapacity > MAXIMUM_CAPACITY)
 7             initialCapacity = MAXIMUM_CAPACITY;//MAXIMUM_CAPACITY为2的30次方
 8         if (loadFactor <= 0 || Float.isNaN(loadFactor))
 9             throw new IllegalArgumentException("Illegal load factor: " +
10                                                loadFactor);
11         this.loadFactor = loadFactor;//将设置的加速因子赋给HashMap加速因子
12         this.threshold = tableSizeFor(initialCapacity);//计算bucket的大小
13     }
 public HashMap(int initialCapacity) {
        //只是设置了HashMap的bucket的大小,加速因子使用默认的
        this(initialCapacity, DEFAULT_LOAD_FACTOR);//调用上述的构造函数
    }
public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;//将加速因子设为默认值
        putMapEntries(m, false);//将Map中的所有值都存放到HashMap中
    }


final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {//判断m是否为空
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)//m的容量大于HashMap的界限值时就调用resize来扩容
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {//历遍m,将所有的元素放入hashMap中去
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

Get和Put方法

Get

简单来说,get方法的核心是通过key.hashcode&(bucket.size-1)来确定桶位,再历遍桶位中列表。

注意

一般来,key.hashcode是唯一且不变的。

key.hashcode&(bucket.size-1)和key.hashcode%(bucket.size-1)都能确桶位,但是前者进行的是位运算会比求余要快一些,而且解释了bucket的大小为什么是2的倍数(如果bucket的大小不是2的倍数,位运算的结果就不对,会造成hash表的混乱。)

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;//判断getNode函数返回值来判断是否存在该值
    }
 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 &&//判断hashmap是否非空
            (first = tab[(n - 1) & hash]) != null)//判断目标值的链表头是否非空
        {
            if (first.hash == hash && // 检查目标值是否和链表头是否相同
                ((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;
    }
Put

简单来说,put方法的核心是通过key.hashcode&(bucket.size-1)来确定桶位,再历遍桶位中列表,找到合适的位置。

注意

在里边过程中,如果发现两个hashcode重复,hashmap一般会认为是一个值,就不会进行增加的操作。

 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)//判断hashmap是否非空
            n = (tab = resize()).length;//将扩容的hashmap的容量赋给n
        if ((p = tab[i = (n - 1) & hash/*计算添加值的hashcode所在的链表头*/]) == 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))))//判断添加值和头链表中元素是否重复(通过hashcode来判断)
                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))))//判断添加值和链表中元素是否重复(通过hashcode来判断)
                        break;
                    p = e;
                }
            }
            if (e != null) { // 节点和添加值重复了
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;//元素的数目增加
        if (++size > threshold)//元素的数目是否超过阀值
            resize();//扩容
        afterNodeInsertion(evict);
        return null;
    }

浅谈HashMap

标签:lse   ini   不同的   时间   toc   出错   public   equal   安全   

原文地址:https://www.cnblogs.com/whllow/p/12189718.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!