浅谈HashMap

HashMap继承AbstractMap抽象类

AbstractMap实现Map接口

HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。

HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单向链表。当准备添加一个key-value对时,首先通过hash(key)方法计算hash值,然后通过indexFor(hash,length)求该key-value对的存储位置,计算方法是先用hash&0x7FFFFFFF后,再对length取模,这就保证每一个key-value对都能存入HashMap中,当计算出的位置相同时,由于存入位置是一个链表,则把这个key-value对插入链表头。

HashTable和HashMap区别

1、继承的父类不同

Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。

2、线程安全性不同

HashMap非线程安全,HashTable为线程安全。

PS:线程安全效率会低,所以项目中非多线程推荐使用HashMap;

从源码角度分析:
HashMap构造方法如下:

public HashMap() {
 this.loadFactor = DEFAULT_LOAD_FACTOR; 
}
static final float DEFAULT_LOAD_FACTOR = 0.75f;

初始化赋值了一个0.75f,指的是存储空间占大于总空间的0.75倍会进行扩容。

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

这段话是说初始化或加倍表大小。如果为空,则分配与初始容量目标保持一致。否则,因为我们使用的是二次展开的幂,所以每个bin中的元素必须保持在同一索引中,或者移动

在新表中使用两个偏移量的幂。(简单来说,就是hashmap必须是2的多少次幂的内存)

我们来看几个常用的方法:

public void clear() {
    Node<K,V>[] tab;
 modCount++;
 if ((tab = table) != null && size > 0) {
 size = 0;
 for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

删除操作,判断不为0,然后循环赋null

public boolean containsValue(Object value) {
    Node<K,V>[] tab; V v;
 if ((tab = table) != null && size > 0) {
 for (int i = 0; i < tab.length; ++i) {
 for (Node<K,V> e = tab[i]; e != null; e = e.next) {
 if ((v = e.value) == value ||
                    (value != null && value.equals(v)))
 return true;
            }
        }
    }
 return false;
}

方法用于检查特定值是由HashMap中的单个键还是多个键映射。它将Value作为参数,如果该值由映射中的任何键映射,则返回True。

我随机介绍2个,这些都是非map方法,hashmap进行重写后的方法。

对了,hashmap默认创建16个存储key/value,大于百分之0.75会进行扩容,每次扩容2倍(一定是2的n次幂),hashmap可以接受手动创建大小,建议初始化大小为128,如果不是2的n次幂,hashmap内部会重新构造。

大家可以关注一下我的微信公众号:菜鸟营

每天更新1-2篇技术性文章,都是我原创,适合刚入门、应届生、初级程序员关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值