HashMap

属性:

  // 初始化table大小16

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 集合大小的最大值
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的加载因子(loadFactor) 加载因子是表示Hash表中元素的填满的程度。  哈希冲突”和“空间利用率”矛盾的一个折衷。
跟数据结构要么查询快要么插入快一个道理,hashmap就是一个插入慢、查询快的数据结构。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//相同hash值的链表元素最多8个 大于8个并且table的长度小于64时就会进行table的扩容
如果table大于64就会将单链表转化为红黑树
static final int TREEIFY_THRESHOLD = 8;
 // 红黑树最少的节点数6个 少于6个就转成链表
static final int UNTREEIFY_THRESHOLD = 6;
不小于64时仅仅对table进行扩容,这个64即使这个值
static final int MIN_TREEIFY_CAPACITY = 64;
Hash的散列表
这是一个table数组,从0,1,2,3。。。到n
里面包含着node节点,里面的节点也可以形成链表或者红黑树
transient Node<K,V>[] table;
// 存放具体元素的集合
transient Set<Map.Entry<K,V>> entrySet;
//HashMap 实际存储的键值对元素个数
transient int size;
hashMap的结构变化次数
transient int modCount;
 //临界值(即hashMap 实际能存储的大小),公式为(threshold = capacity * loadFactor)
当size大于这个数值时进行扩容table
int threshold;

//HashMap 加载因子,当table中的被占用元素与table总长度的比列不小于这个参数的时候,就会进行table的扩容,以两倍扩容。
final float loadFactor;




构造函数:
构造函数1
默认填充因子,默认长度16

 

构造函数2;

默认的填充因子,指定的长度


 

构造函数3:
指定的长度,指定的填充因子

 构造方法4:

从另外一个map中映射拷贝一份到这个存储结构里面


 

方法:
当构造函数传入map时,会调用这个函数初始化一些参数
   final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            // 初始table为null
            if (table == null) { // pre-size
                // 用负载参数进行计算
                float ft = ((float)s / loadFactor) + 1.0F;
                // 与最大容量作比较 返回对应的int类型值
                int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY);
                // The next size value at which to resize (capacity * load factor).
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            // 扩容
            else if (s > threshold)
                resize();
            // 插入处理
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

 这里的扩容类似于ArrayList的grow函数,不同的是这里扩容的算法是每次乘以2,并且存在一个负载参数来修正初次扩容的步数。

  threshold可以看注释,这是一个扩容临界值。当容器大小大于这个值时,就会进行resize扩容操作,临界值取决于当前容器容量与负载参数。

  接下来应该要进入resize函数,参照之前的ArrayList源码,这里也是先扩容得到一个新的数组,然后将所有节点进行转移。

  函数有点长,一步一步来:



返回大小:

 判断是否为空

 


查找元素:

 

是否包含某个元素:

 

 

插入元素:

 

 

 

 

 

 扩容:

 

 

 

这个函数判断需不需要改变冲突节点的存储结构,这个函数会先判断hash的长度,如果不足64,则只进行扩容table,如果达到64则将链表改为红黑树

 



将一个map的元素都加入到这个map里面:

 

移除键为key的元素:

 

 



清空map:

 



判断是否包含某个元素:

 



遍历 HashMap 的过程中会发现,多次对 HashMap 进行遍历时,遍历结果顺序都是一致的。但这个顺序和插入的顺序一般都是不一致的。
和着两个方法有关

 



// 返回“value集合”,实际上返回的是一个Values对象

 

 

 

// 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象

 

 

 



map中有key就用key对应的value,不然就用默认的值

 





而putIfAbsent在放入数据时,如果存在重复的key,那么putIfAbsent不会放入值。

 

删除某个值:

 

替换某个key的值

替换值:

 




和put相似:

不同的是,put只是简单的添加,当map中存在对应Key的时候,put会覆盖掉原本的value值。而computeIfAbsent顾名思义,会检查map中是否存在Key值,如果存在会检查value值是否为空,如果为空就会将K值赋给value。


 


computeIfPresent 的方法,对 指定的 在map中已经存在的key的value进行操作。只对已经存在key的进行操作

 





compute的方法,指定的key在map中的值进行操作 不管存不存在。

 





如果给定key没绑定值或值为null,则绑定给定值,否则,执行重映射方法替换原来值或者删除原来的值


 



遍历:

 

 

 

全部替换:

 

 克隆:

 

 

 迭代器:

 

 

 

 

 

 

 

 

 

 

 参考:

https://blog.csdn.net/weixin_41064826/article/details/79705324

https://segmentfault.com/a/1190000012926722

https://blog.csdn.net/sswltt/article/details/92830309

https://www.cnblogs.com/dongying/p/4022795.html

https://www.cnblogs.com/skywang12345/p/3310835.html

https://cloud.tencent.com/developer/article/1337158

https://wenku.baidu.com/view/6e1035943968011ca30091cd.html


https://www.cnblogs.com/QH-Jimmy/p/7804356.html


posted @ 2019-06-21 16:51  远方的人111  阅读(160)  评论(0)    收藏  举报