您的位置:首页 > 编程语言 > Java开发

集合扩容问题(ArrList为例,常用集合扩容机制) -- JAVA 基础

2017-11-07 13:55 501 查看
http://m.blog.csdn.net/github_39500681/article/details/78061724

今天刚好遇到一个关于集合扩容的问题,正好借机整理一下:

当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,如果是每增加一个元素就复制一次,这无疑使效率大大降低。这里的扩容就是当当前容器的内存不足或者达到加载因子设置的系数时,需要对容器进行一次合适的扩建(加载因子的系数小于等于1,意指 即当 元素个数 超过 容量长度*加载因子的系数 时,进行扩容。有默认值)

List集合 (元素是有序的、可重复)

我们知道List集合的底层实现是数组结构,而数组的大小是不可改变的,因此当其容器内存不足时,需要进行扩容,扩容的方法就是重新分配一个新数组,然后复制元素到新数组中,再将元素添加到数组末位

一、ArrayList

首先便是默认大小的问题

这里我们可以通过ArrayList的源码来分析,首先可以看一下他的构造函数,发现有三个,分别是:无参、一个int型参数、一个集合泛型;

在分析构造函数之前 我们先来看几个参数:



即ArrayList 在内部的表示即为一个Object类型的数组,并非真正的泛型,这里的变量就是数组的真实存储大小



可以理解为设置默认数组大小的常量

1、无参的构造函数



在这里我们可以看到,当不传参数时,数组的大小直接为默认数组大小的常量,而这个大小就为10,至于怎么得到的,这里我们可以先卖个小关子,在后面一起介绍~~
2、有一个int的构造函数



我们可以发现,当传入的值大于0时,数组的大小即为传入的值大小,为0时,则为EMPTY_ELEMENTDATA,小于0则抛出异常。这个EMPTY_ELEMENTDATA又是什么意思呢??

其实就是0(即亦可理解为传入的值);

3、有一个Collection泛型的构造函数



而集合参数的其实与Int的构造函数差不多,只是在初始化容器的同时,还将容器中的数据也初始化了(完成copy)。

虽然说完了构造函数,但实际的扩容是发生在增加元素的时候(容器内存不足或者存储达到加载因子),因此 来到了最为主要的一部分内容:
4、ArrayList 的元素添加



通过上面的代码可以看到,每次添加元素的时候,都会执行一个方法,而这个方法,就是判断是否需要扩容的方法;



进入方法之后,我们看到了一个if语句,发现了么,这个就是我们之前在无参构造函数时卖的关子,当当前的大小为默认数组大小的常量时,minCapacity(即位数组中存储的数据大小即size+1)就为DEFAULT_CAPACITY, minCapacity之间大的那个,而minCapacity的默认值为0,所以从这里就可以知道,无参构造函数产生的数组大小为10~~

但是真正的判断还不在这里,而是下面这个方法:



由此我们可以看出,其实就是判断加一个元素后,数组大小是够超出当前数组的大小,若超出,则执行下面的”扩容“代码~~



从文中我们可看到,新数组的大小其实就是( oldCapacity + (oldCapacity >> 1)),如果你懂得位运算的话,你就知道了,原来就是原数组的长度加上原数组的长度大小的一半(位运算,右移一位相当于整体/2),后面判断数组大小越界这里就不多阐述了,感兴趣的可以自己查看一下源码。

上面的ArrayList是没有涉及加载因子的,但是有些集合类型如Vector等使用到了加载因子,只要把是否需要扩容d的判断条件改一下就可以了。

**

总结

**

ArrayList 默认初始容量为10

线程不安全,查询速度快

    底层数据结构是数组结构

    扩容增量:原容量的 0.5倍

    如 ArrayList的容量为10,一次扩容后是容量为15

同样可以通过分析源码知道:

Vector:线程安全,但速度慢

    底层数据结构是数组结构

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

    扩容增量:原容量的 1倍

    如 Vector的容量为10,一次扩容后是容量为20

Set(集) 元素无序的、不可重复。

HashSet:线程不安全,存取速度快

     底层实现是一个HashMap(保存数据),实现Set接口

     默认初始容量为16(为何是16,见下方对HashMap的描述)

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

     扩容增量:原容量的 1 倍

      如 HashSet的容量为16,一次扩容后是容量为32

在JDK1.8中我们可以发现其实HashSet 的创建其实就是一个HashMap



Map是一个双列集合

HashMap:默认初始容量为16

     (为何是16:16是2^4,可以提高查询效率,另外,32=16<<1 –>至于详细的原因可另行分析,或分析源代码)

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

     扩容增量:原容量的 1 倍

      如 HashSet的容量为16,一次扩容后是容量为32

ps:文章末尾参考http://blog.csdn.net/caiwanxia1/article/details/52949802 感兴趣的可以去原博文~~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: