在面试后台开发的过程当中,集合是面试的热话题,不只要知道各集合的区别用法,还要知道集合的扩容机制,今天咱们就来谈下ArrayList 和 HashMap的默认大小以及扩容机制。java
在 Java 7 中,查看源码能够知道:ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂,为何呢???下文有解释)。这就是 Java 7 中 ArrayList 和 HashMap 类 的代码片断:面试
// from ArrayList.java JDK 1.7 private static final int DEFAULT_CAPACITY = 10; //from HashMap.java JDK 7 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
这里要讨论这些经常使用的默认初始容量和扩容的缘由是:数组
当底层实现涉及到扩容时,容器或从新分配一段更大的连续内存(若是是离散分配则不须要从新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据所有复制到新的内存上,安全
这无疑使效率大大下降。加载因子的系数小于等于1,意指即当元素个数超过容量长度*加载因子的系数时,进行扩容。另外,扩容也是有默认的倍数的,不一样的容器扩容状况不一样。数据结构
List 元素是有序的、可重复性能
ArrayList、Vector默认初始容量为10spa
Vector:线程安全,但速度慢线程
底层数据结构是数组结构code
加载因子为1:即当 元素个数 超过 容量长度 时,进行扩容blog
扩容增量:原容量的 1倍
如 Vector的容量为10,一次扩容后是容量为20
ArrayList:线程不安全,查询速度快
底层数据结构是数组结构
扩容增量:原容量的 0.5倍+1
如 ArrayList的容量为10,一次扩容后是容量为16
Set(集) 元素无序的、不可重复。
HashSet:线程不安全,存取速度快
底层实现是一个HashMap(保存数据),实现Set接口
默认初始容量为16(为什么是16,见下方对HashMap的描述)
加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容
扩容增量:原容量的 1 倍
如 HashSet的容量为16,一次扩容后是容量为32
Map是一个双列集合
HashMap:默认初始容量为16
(为什么是16:16是2^4,能够提升查询效率,另外,32=16<<1)
加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容
扩容增量:原容量的 1 倍
如 HashSet的容量为16,一次扩容后是容量为32
hashMap的数组长度必定保持2的次幂,好比16的二进制表示为 10000,那么length-1就是15,二进制为01111,同理扩容后的数组长度为32,二进制表示为100000,length-1为31,二进制表示为011111。
这样会保证低位全为1,而扩容后只有一位差别,也就是多出了最左位的1,这样在经过 h&(length-1)的时候,只要h对应的最左边的那一个差别位为0,就能保证获得的新的数组索引和老数组索引一致(大大减小了
以前已经散列良好的老数组的数据位置从新调换),还有,数组长度保持2的次幂,length-1的低位都为1,会使得得到的数组索引index更加均匀。
1. static int indexFor(int h, int length) { 2. return h & (length-1); 3. }
首先算得key得hashcode值,而后跟数组的长度-1作一次“与”运算(&)。看上去很简单,其实比较有玄机。好比数组的长度是2的4次方,那么hashcode就会和2的4次方-1作“与”运算。不少人都有这个疑问,
为何hashmap的数组初始化大小都是2的次方大小时,hashmap的效率最高,我以2的4次方举例,来解释一下为何数组大小为2的幂时hashmap访问的性能最高。
看下图,左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,可是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同
一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就须要遍历这个链表,获得8或者9,这样就下降了查询的效率。同时,咱们也能够发现,当数组长度为15的时候,hashcode的
值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费至关大,更糟的是这种状况中,数组可使用的位置比数组
长度小了不少,这意味着进一步增长了碰撞的概率,减慢了查询的效率!
因此说,当数组长度为2的n次幂的时候,不一样的key算得得index相同的概率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的概率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。
说到这里,咱们再回头看一下hashmap中默认的数组大小是多少,查看源代码能够得知是16,为何是16,而不是15,也不是20呢,看到上面的解释以后咱们就清楚了吧,显然是由于16是2的整数次幂的缘由,
在小数据量的状况下16比15和20更能减小key之间的碰撞,而加快查询的效率。