程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

ArrayList扩容机制

发布于2021-05-29 21:11     阅读(920)     评论(0)     点赞(4)     收藏(1)


ArrayList简介:

    ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多种方法可以对数据进行操作但是它不是线程安全的,ArrayList按照插入的顺序来存放数据。

1.成员变量

  1. // 默认给定的初始容量
  2.     private static final int DEFAULT_CAPACITY = 10;
  3.  
  4.     // 无参构造器中所使用到的空数组实例
  5.     private static final Object[] EMPTY_ELEMENTDATA = {};
  6.  
  7.     // 有参构造器中所使用到的空数组实例
  8.     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  9.  
  10.     // 数据元素集合
  11.     transient Object[] elementData;
  12.  
  13.     //当前数组的长度
  14.     private int size;

2.构造方法

  1. /**
  2. * 构造一个初始容量为10的空列表
  3. */
  4. public ArrayList() {
  5. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  6. }
  7. /**
  8. * 构造一个具有指定容量大小的空列表
  9. */
  10. public ArrayList(int initialCapacity) {
  11. if (initialCapacity > 0) {
  12. this.elementData = new Object[initialCapacity];
  13. } else if (initialCapacity == 0) {
  14. this.elementData = EMPTY_ELEMENTDATA;
  15. } else {
  16. throw new IllegalArgumentException("Illegal Capacity: "+
  17. initialCapacity);
  18. }
  19. }
  20. /**
  21. * 构造一个包含指定集合的元素的列表
  22. */
  23. public ArrayList(Collection<? extends E> c) {
  24. elementData = c.toArray();
  25. if ((size = elementData.length) != 0) {
  26. // c.toArray might (incorrectly) not return Object[] (see 6260652)
  27. if (elementData.getClass() != Object[].class)
  28. elementData = Arrays.copyOf(elementData, size, Object[].class);
  29. } else {
  30. // replace with empty array.
  31. this.elementData = EMPTY_ELEMENTDATA;
  32. }
  33. }

3.扩容机制

ArrayList扩容的核心从ensureCapacityInternal方法说起。

可以看到前面介绍成员变量的提到的ArrayList有两个默认的空数组:

DEFAULTCAPACITY_EMPTY_ELEMENTDATA:是用来使用默认构造方法时候返回的空数组。如果第一次添加数据的话那么数组扩容长度为DEFAULT_CAPACITY=10。

EMPTY_ELEMENTDATA:出现在需要用到空数组的地方,其中一处就是使用自定义初始容量构造方法时候如果你指定初始容量为0的时候就会返回。

从下面可以看到如果是使用了空数组EMPTY_ELEMENTDATA话,那么不会返回默认的初始容量。

  1. //判断当前数组是否是默认构造方法生成的空数组,如果是的话minCapacity=10反之则根据原来的值传入下一个方法去完成下一步的扩容判断
  2. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  3. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  4. return Math.max(DEFAULT_CAPACITY, minCapacity);
  5. }
  6. return minCapacity;
  7. }
  8. //minCapacitt表示修改后的数组容量,minCapacity = size + 1
  9. private void ensureCapacityInternal(int minCapacity) {
  10. //判断看看是否需要扩容
  11. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  12. }

下面谈谈ensureExplicitCapacity方法(modCount设计到Java的快速报错机制后面会谈到),可以看到如果修改后的数组容量大于当前的数组长度那么就需要调用grow进行扩容,反之则不需要。

  1. //判断当前ArrayList是否需要进行扩容
  2. private void ensureExplicitCapacity(int minCapacity) {
  3. //快速报错机制
  4.  modCount++;
  5. // overflow-conscious code
  6. if (minCapacity - elementData.length > 0)
  7. grow(minCapacity);
  8. }

最后看下ArrayList扩容的核心方法grow(),下面将针对三种情况对该方法进行解析:

  1. private void grow(int minCapacity) {
  2. // overflow-conscious code
  3. int oldCapacity = elementData.length;
  4. int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容至1.5倍
  5. if (newCapacity - minCapacity < 0)
  6. newCapacity = minCapacity;
  7. if (newCapacity - MAX_ARRAY_SIZE > 0)
  8. newCapacity = hugeCapacity(minCapacity);
  9. // minCapacity is usually close to size, so this is a win:
  10. elementData = Arrays.copyOf(elementData, newCapacity);
  11. }
  12. private static int hugeCapacity(int minCapacity) {
  13. if (minCapacity < 0) // overflow
  14. throw new OutOfMemoryError();
  15. return (minCapacity > MAX_ARRAY_SIZE) ?
  16. Integer.MAX_VALUE :
  17. MAX_ARRAY_SIZE;
  18. }

1>当前数组是由默认构造方法生成的空数组并且第一次添加数据。

    此时从方法 calculateCapacity中可以看出,minCapacity== 10,当数组长度大于10的时候才会进入到grow方法中进行扩容(按照当前容量的1.5倍进行扩容);

2>当前数组是由自定义初始容量构造方法创建并且指定初始容量为0。

    此时minCapacity == 1,elementData.length == 0,

    方法grow中,minCapacity ==1   oldCapacity == 0    newCapacity < 0  =》newCapacity = 1

                          minCapacity == 2  oldCapacity == 1    newCapacity == 1.5  =》newCapacity = 2

                         minCapacity == 3  oldCapacity == 2    newCapacity == 3  =》newCapacity = 3

                         minCapacity == 4  oldCapacity == 3    newCapacity == 4.5  =》newCapacity = 4.5

                         minCapacity == 5  oldCapacity == 4    newCapacity == 6  =》newCapacity = 6

那么根据下面逻辑可以看到最后数组的容量会从0变成1。根据上述算法前四次扩容每次都 +1,在第5次添加数据进行扩容的时候才是按照当前容量的1.5倍进行扩容。
3>当扩容量(newCapacity)大于ArrayList数组定义的最大值后会调用hugeCapacity来进行判断。如果minCapacity已经大于Integer的最大值(溢出为负数)那么抛出OutOfMemoryError(内存溢出)否则的话根据与MAX_ARRAY_SIZE的比较情况确定是返回Integer最大值还是MAX_ARRAY_SIZE。这边也可以看到ArrayList允许的最大容量就是Integer的最大值(-2的31次方~2的31次方减1)。



所属网站分类: 技术文章 > 博客

作者:我是小豆丁

链接:http://www.javaheidong.com/blog/article/207515/9e99452ebf3d22242816/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

4 0
收藏该文
已收藏

评论内容:(最多支持255个字符)