Java常见集合的默认大小及扩容机制

在面试后台开发的过程中,集合是面试的热话题,不仅要知道各集合的区别用法,还要知道集合的扩容机制,今天我们就来谈下ArrayList 和 HashMap的默认大小以及扩容机制。

在 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默认初始容量为10

Vector:线程安全,但速度慢

    底层数据结构是数组结构

    加载因子为1:即当 元素个数 超过 容量长度 时,进行扩容

    扩容增量:原容量的 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的次幂?

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之间的碰撞,而加快查询的效率。

原文地址:https://www.cnblogs.com/whu-2017/p/9677212.html

时间: 2024-10-30 22:51:46

Java常见集合的默认大小及扩容机制的相关文章

java常见的集合框架有哪些?

java常见的集合框架有哪些?很多学习java的朋友会问到这个问题,那么学习Java集合框架下大致可以分为如下五个部分:List列表.Set集合.Map映射.迭代器(Iterator.Enumeration).工具类(Arrays.Collections).今天西安java培训小编为大家分享. Java集合类的整体框架如下: 从上图中可以看出,集合类主要分为两大类:Collection和Map. Collection是List.Set等集合高度抽象出来的接口,它包含了这些集合的基本操作,它主要又

Java基础——集合源码解析 List List 接口

今天我们来学习集合的第一大体系 List. List 是一个接口,定义了一组元素是有序的.可重复的集合. List 继承自 Collection,较之 Collection,List 还添加了以下操作方法 位置相关:List 的元素是有序的,因此有get(index).set(index,object).add(index,object).remove(index) 方法. 搜索:indexOf(),lastIndexOf(); 迭代:使用 Iterator 的功能板迭代器 范围性操作:使用 s

【面试必备】透过源码角度一步一步带你分析 ArrayList 扩容机制

一 先从 ArrayList 的构造函数说起ArrayList有三种方式来初始化,构造方法源码如下:/** 默认初始容量大小*/private static final int DEFAULT_CAPACITY = 10; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /*默认构造函数,使用初始容量10构造一个空列表(无参数构造)*/public ArrayList() {this.elementDat

Java小白集合源码的学习系列:ArrayList

目录 ArrayList源码学习 ArrayList的继承体系 ArrayList核心源码 ArrayList扩容机制 最后的总结 ArrayList源码学习 本文基于JDK1.8版本,对集合中的巨头ArrayList做一定的源码学习,将会参考大量资料,在文章后面都将会给出参考文章链接,本文用以巩固学习知识. ArrayList的继承体系 ArrayList继承了AbstracList这个抽象类,还实现了List接口,提供了添加.删除.修改.遍历等功能.至于其他接口,以后再做总结. ArrayL

Java小白集合源码的学习系列:Vector

目录 Vector源码学习 Vector继承体系 Vector核心源码 基本属性 构造器 扩容机制 Enumeration 概述 源码描述 具体操作 Vector总结 Vector源码学习 前文传送门: Java小白集合源码的学习系列:LinkedList Java小白集合源码的学习系列:ArrayList Vector是JDK1.0中的集合,是集合中的老大哥,其中大部分的方法都被synchronized关键字所修饰,与ArrayList和LinkedList不同,它是线程安全的(关于线程安全,

java的集合框架之一

java是一套很成熟的东西,很多商用的东西都喜欢用它,用的人多,稳定.不过一般也不怎么说起它,因为太常见了,私下里说,写java应用层得就像农民工,每一处都是搭积木,根据设计师的东西如何优雅地搭好积木,当然美其名曰,论农民工搭积木的艺术修养.不难,但是东西很多,经验在里面是一个相当重要的东西.以上纯属每天扯淡,笑看即可,毕竟我目前就是个渣java程序员. java的集合框架以collection接口当作基础,这个接口定义了基本框架,包括size.hashcode.iterator.add.add

java的集合框架最全详解

java的集合框架最全详解(图) 前言:数据结构对程序设计有着深远的影响,在面向过程的C语言中,数据库结构用struct来描述,而在面向对象的编程中,数据结构是用类来描述的,并且包含有对该数据结构操作的方法. 在Java语言中,Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类).所有抽象出来的数据结构和操作(算法)统称为Java集合框架(JavaCollectionFramework). Java程序员在具体应用时,不必考虑数据结构和算法实现细节,只需要用这

Java的集合框架最全详解(图)

纯个人整理,如有错误请指正. java的集合框架最全详解(图) 前言:数据结构对程序设计有着深远的影响,在面向过程的C语言中,数据库结构用struct来描述,而在面向对象的编程中,数据结构是用类来描述的,并且包含有对该数据结构操作的方法. 在Java语言中,Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类).所有抽象出来的数据结构和操作(算法)统称为Java集合框架(JavaCollectionFramework). Java程序员在具体应用时,不必考虑数据

常见集合知识

java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap. Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复.Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的. HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;