分享知识-快乐自己:HashMap 与 HashTable 的区别
Posted mlq2017
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分享知识-快乐自己:HashMap 与 HashTable 的区别相关的知识,希望对你有一定的参考价值。
特性:
HashMap 与 Hashtable 的分析:
1):HashMap简介
1、底层数组+链表实现,可以存储null键和null值,线程不安全
2、HashMap 不是线程安全的
3、HashMap 是 map 接口的子类。
4、HashMap 允许null key 和 null value。
5、允许 key 重复,但是会把之前的覆盖。
6、HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),他们都完成了Map接口。
7、HashMap 是 reHash算法,而 HashTable 是 hash算法。两者差不多
8、HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsvalue 和 containsKey。因为contains方法容易让人引起误解。
HashMap结构图:
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(如果不同的key映射到了数组的同一位置处,就将其放入单链表中)。
如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,
仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
几个比较重要的字段:
//实际存储的key-value键值对的个数
transient int size;
//阈值,当table == {}时,该值为初始容量(初始容量默认为16);当table被填充了,也就是为table分配内存空间后,threshold一般为 capacity*loadFactory。
//HashMap在进行扩容时需要参考threshold,后面会详细谈到
int threshold;
//负载因子,代表了table的填充度有多少,默认是0.75
final float loadFactor;
//用于快速失败,由于HashMap非线程安全,在对HashMap进行迭代时,如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),需要抛出异常ConcurrentModificationException
transient int modCount;
HashMap有4个构造器,其他构造器如果用户没有传入 initialCapacity 和 loadFactor这两个参数,会使用默认值 initialCapacity默认为16,loadFactory默认为0.75
我们看下其中一个:
public HashMap(int initialCapacity, float loadFactor) {
//此处对传入的初始容量进行校验,最大不能超过MAXIMUM_CAPACITY = 1<<30(230)
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();//init方法在HashMap中没有实际实现,不过在其子类如 linkedHashMap中就会有对应实现
}
从上面这段代码我们可以看出,在常规构造器中,没有为数组 table 分配内存空间(有一个入参为指定Map的构造器例外),而是在执行 put 操作的时候才真正构建 table 数组 OK。
接下来我们来看看put操作的实现吧。
public V put(K key, V value) {
//如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,此时threshold为initialCapacity 默认是1<<4(24=16)
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
//如果key为null,存储位置为table[0]或table[0]的冲突链上
if (key == null)
return putForNullKey(value);
int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀
int i = indexFor(hash, table.length);//获取在table中的实际位置
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
//如果该对应数据已存在,执行覆盖操作。用新value替换旧value,并返回旧value
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败
addEntry(hash, key, value, i);//新增一个entry
return null;
}
2):HashTable介绍
1、底层数组+链表实现.
2、无论 key 还是 value 都不能为 null。
3、线程安全:实现线程安全的方式是在修改数据时锁住整个HashTable,效率低。
4、Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题。
5、hashtable 采用 hash 算法。
以上是关于分享知识-快乐自己:HashMap 与 HashTable 的区别的主要内容,如果未能解决你的问题,请参考以下文章