扩容(加载)因子为何默认是 0.75f
- 在空间占用与查询时间之间取得较好的权衡
- 大于这个值,空间节省了,但链表就会比较长影响性能
- 小于这个值,冲突减少了 , 但扩容就会更频繁,空间占用也更多
- 扩容死链(1.7 会存在): 原因是1.7头插法会改变元素顺序
- 数据错误(1.7和1.8):并发环境下若多个待插入元素计算出来的索引下标相同则会产生覆盖问题
- HashMap 的 key 可以为 null , 但 Map 的其他实现则不然
- 作为 key 的对象,必须实现 hashCode 和 equals,并且 key 的内容不能修改(不可变)
- key 的 hashCode 应该有良好的散列性
几种Map接口实现类的区别1. HashMap 和 Hashtable 的区别
- 线程是否安全:
HashMap
是非线程安全的,Hashtable
是线程安全的 , 因为Hashtable
内部的方法基本都经过synchronized
修饰 。(如果你要保证线程安全的话就使用ConcurrentHashMap
吧?。?/li> - 效率: 因为线程安全的问题,
HashMap
要比Hashtable
效率高一点 。另外,Hashtable
基本被淘汰,不要在代码中使用它; - 对 Null key 和 Null value 的支持:
HashMap
可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出NullPointerException
。 - 初始容量大小和每次扩充容量大小的不同 :① 创建时如果不指定容量初始值 ,
Hashtable
默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1 。HashMap
默认的初始化大小为 16 。之后每次扩充,容量变为原来的 2 倍 。② 创建时如果给定了容量初始值 , 那么 Hashtable 会直接使用你给定的大?。?而HashMap
会将其扩充为 2 的幂次方大?。?code>HashMap 中的tableSizeFor()
方法保证,下面给出了源代码) 。也就是说HashMap
总是使用 2 的幂作为哈希表的大小 , 后面会介绍到为什么是 2 的幂次方 。 - 底层数据结构: JDK1.8 以后的
HashMap
在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断 , 如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间 。Hashtable 没有这样的机制 。
HashMap
中带有初始容量的构造函数:public HashMap(int initialCapacity,float loadFactor) {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;this.threshold = tableSizeFor(initialCapacity);}public HashMap(int initialCapacity) {this(initialCapacity,DEFAULT_LOAD_FACTOR);}
下面这个方法保证了 HashMap
总是使用 2 的幂作为哈希表的大小 。/*** Returns a power of two size for the given target capacity.*/static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}
2. HashMap 和 HashSet 区别如果你看过 HashSet
源码的话就应该知道:HashSet
底层就是基于 HashMap
实现的 。(HashSet
的源码非常非常少,因为除了 clone()
、writeObject()
、readObject()
是 HashSet
自己不得不实现之外 , 其他方法都是直接调用 HashMap
中的方法 。HashMap
HashSet
实现了 Map
接口实现 Set
接口存储键值对仅存储对象调用 put()
向 map 中添加元素调用 add()
方法向 Set
中添加元素HashMap
使用键(Key)计算 hashcode
HashSet
使用成员对象来计算 hashcode
值,对于两个对象来说 hashcode
可能相同,所以equals()
方法用来判断对象的相等性3. HashMap 和 TreeMap 区别TreeMap
和HashMap
都继承自AbstractMap
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Java单例模式,看这一篇就够了
- 微信支付v3接口的 官方 Java SDK
- Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化
- 夯实Java基础,一篇文章全解析线程问题
- 四 Java多线程-ThreadPool线程池-2
- 生成器函数 javascript异步编程之generator与asnyc/await语法糖
- Java Timer使用介绍
- 三 Java多线程-ThreadPool线程池
- 二 Java多线程-线程关键字
- 二 Java 编码那些事