zoukankan      html  css  js  c++  java
  • 【学习笔记-集合】HashMap 源码浅析

    /**
     * HashMap主要方法解析,jdk1.7版本的HashMap
     * HashMap数据是通过数组和链表结合的方式(链表散列)存储。
     * 在put时候根据key值得到hash值(地址)即数组下标,之后如果得到相同下标则放在链表前面,之前的数据在链表尾部。                   
     * 前的数据在链表尾部。  
     *  在查找数据时候根据hashcode获取数组下标,在链表中使用equals根据key查找value.
     * 一、构造
     * 4个构造相对之前的jdk版本功能基本不变,但是代码封装更完善。
     * 构造前一个参数是容量,相当于数组大小,后一个是负载因子
     */
    public HashMap(int initialCapacity, float loadFactor) {
            //当初始容量<0,抛出异常非法的参数容量
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            //初始容量不能大于最大容量值,最大容量值为MAXIMUM_CAPACITY = 1 << 30;   
            //左移一位相当于乘以2,所以左移30位相当于2^30.
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            //负载因子不为空并且<=0
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
            //保存参数并且初始化数组
            this.loadFactor = loadFactor;
            threshold = initialCapacity;
            //此初始化将插入数据,主要使用Entry
            init();
        }
    //无参构造默认容量是16,负载因子0.75
    public HashMap() {
            this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
        }
    //指定容量参数,默认负载因子0.75
    public HashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }
    /构造与指定map相同映射的新HashMap 
    public HashMap(Map<? extends K, ? extends V> m) {
            this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                          DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
            //如果容量不够,扩大table数组
            inflateTable(threshold);
            //将map中的元素添加到HashMap中
            putAllForCreate(m);
        }
     
    /**
     * 二、创建数据链
     * 静态内部类,包含键值,节点next和hash值,由于他的存在,才会让table数组项以链表方式存在
     */
    static class Entry<K,V> implements Map.Entry<K,V> {
            final K key;
            V value;
            Entry<K,V> next;
            int hash;
     
            //添加新条目
            Entry(int h, K k, V v, Entry<K,V> n) {
                value = v;
                next = n;
                key = k;
                hash = h;
            }
     
            public final K getKey() {
                return key;
            }
     
            public final V getValue() {
                return value;
            }
     
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
            。
            。
            。
            。
            。
     /**
      *  三、存
      *  实现快速存取
      *  添加数据
      */
    public V put(K key, V value) {
            //如果数组为空,添加数组容量
            if (table == EMPTY_TABLE) {
                inflateTable(threshold);
            }
            //如果key为空,保存null在table的第一个位置,所以HashMap可以为null
            if (key == null)
                return putForNullKey(value);
            //计算hash值
            int hash = hash(key);
            //计算key的hash值在table中的位置(索引)
            int i = indexFor(hash, table.length);
            //从i迭代e,找到key保存的位置
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                //判断该链上是否有hash(key)值相同的情况,若存在,则将其value值覆盖,保留新value
                //新值等于旧值,返回旧值
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
            //修改次数加一
            modCount++;
            //将key,value值添加在i处
            addEntry(hash, key, value, i);
            return null;
        }
    //都是添加Entry,这个是HashMap实际容量超过容器容量,下面的方法是没超过的情况
    void addEntry(int hash, K key, V value, int bucketIndex) {
            //当HashMap大小不小于临界值(容量*负载因子)大小,并且数组bucketIndex位置不为null,改变HashMap大小,记录索引
            if ((size >= threshold) && (null != table[bucketIndex])) {
                resize(2 * table.length);
                hash = (null != key) ? hash(key) : 0;
                bucketIndex = indexFor(hash, table.length);
            }
            //否则调用createEntry方法
            createEntry(hash, key, value, bucketIndex);
        }
     
    //HashMap实际容量未超过默认容量或者初始化容量
    void createEntry(int hash, K key, V value, int bucketIndex) {
            //保存bucketIndex的所在位置到e中
            Entry<K,V> e = table[bucketIndex];
            //设置bucketIndex位置元素为新Entry,并且设置e为新Entry下一个节点
            table[bucketIndex] = new Entry<>(hash, key, value, e);
            size++;
        }
    /**
      *  四、取
      *  实现快速存取
      *  获取数据
      */
    public V get(Object key) {
            //若key为null,调用getForNullKey取出value
            if (key == null)
                return getForNullKey();
            //根据key值算出hash值并取出table对应索引处的值
            Entry<K,V> entry = getEntry(key);
     
            return null == entry ? null : entry.getValue();
        }
     
    final Entry<K,V> getEntry(Object key) {
            if (size == 0) {
                return null;
            }
            //计算hash值
            int hash = (key == null) ? 0 : hash(key);
            //根据hash值取出table对应索引处的值
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            }
            return null;
        }
    业精于勤,荒于嬉;行成于思,毁于随;
  • 相关阅读:
    【转】内部Handler类引起内存泄露
    检测是否存在相机硬件代码
    asp.net 过滤器
    iis 中经典和集成模式对应webconfig节点
    事务
    C# Excel操作
    一步一步部署SSIS包图解教程
    js和.net操作Cookie遇到的问题
    File,FileInfo,Directory,DirectoryInfo
    C#文件Copy
  • 原文地址:https://www.cnblogs.com/freedom-yuxin/p/7448835.html
Copyright © 2011-2022 走看看