新年快乐,干货储备充足了吗?--- ArrayList解析

       今天大年初一,大家都在忙着拜年吧!在这里祝大家新年快乐,鼠年大吉!!!除了拜年,相信也有很多人正在储备干货,为了假期之后的金三银四做准备。当然我也没有闲着,在家里忙着过年的空闲时间里面,多学习新的知识,打一场轰轰烈烈的有准备的仗。

       在学习的过程中,我也在不断地思考,我们为什么要学习?学习能给我们带来什么?慢慢的我也想通了,想明白了。其实在学习的过程中,我们就像是在一篇汪洋的大海中遨游,刚开始我们接触技术的时候,犹如在海面上面,看到了海上美丽的风景,我们为之感叹世界的奇妙与鬼斧神工。我们一点点的了解这个世界,当你对周围的一切都已经看遍了的时候,我们又不希望止步于此,希望向更远的地方前行,看不一样的风景。于是你就不断的学习新的知识,在项目中用我们所学的知识去实践。我们在这个海洋上面用知识所造就的小船、游艇遨游,这些我们知识停留在知识的表面,学会怎么使用它们,知其然不知所以然而已。当你的好奇心不满足于此的时候,不满足海面的风景的时候,想要学习底层知识的时候。你就会放弃你的小船、游艇,纵身跳入无边无际的海洋里面去,一点一点的向海底进军,可能开始的时候我们下游的缓慢,难忍的无法呼吸等重重困难,但是当你到达海底的那一瞬间,你就会爱上这个海底世界,就会发觉前面的努力都是值得的,为了这一刻。这就是我学习的感悟。好了,闲聊了这么多的,我还是讲讲最近的收获。

下面是JAVA技术人员的干货时间:

(1)ArrayList的最大容量为(Integer.MAX_VALUE - 8),但容量超过了MAX_ARRAY_SIZE的时候再扩容为Integer.MAX_VALUE大小,我们可能一直以为ArrayList没有最大容量限制,后来看了源码jdk8才知道有一个数组最大容量限制常量,尝试分配更大的阵列可能会导致 OutOfMemoryError异常。源码如下:

   /**
     * 要分配的最大数组大小。 一些虚拟机在数组中保留一些标题字。
     * 但是扩容容量大小超过了MAX_ARRAY_SIZE值的时候会调用hugeCapacity方法,
     * 方法里面满足条件就会扩容为Integer.MAX_VALUE的值
     * 尝试分配更大的阵列可能会导致 OutOfMemoryError:请求的阵列大小超出VM限制
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    // 超过了MAX_ARRAY_SIZE的大小的时候,扩容为Integer.MAX_VALUE的大小
    private static int hugeCapacity(int minCapacity) {
        // 参数小于0,抛出OutOfMemoryError异常
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // 容量大于MAX_ARRAY_SIZE的时候,扩容为Integer.MAX_VALUE,否则为MAX_ARRAY_SIZE
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

(2)ArrayList的默认容器大小为10,源码如下:

// 默认容器大小
private static final int DEFAULT_CAPACITY = 10;
 

(3)ArrayList使用构造函数进行初始化的时候

  • 未传容量大小参数是使用ArrayList默认大小,初始对象为共享变量DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  • 传容量大小参数的时候,容器大小参数为0则初始对象为共享变量EMPTY_ELEMENTDATA,不为0的时候初始化对象数组

为什么会用两种不同的共享变量呢? 因为为了区分两种情况,使用了默认容量大小初始化和未使用默认容量初始化,如果初始对象和DEFAULTCAPACITY_EMPTY_ELEMENTDATA引用地址是一样的话,说明使用默认容量大小10,就会设置size=10.如果不区分开怎么知道你使用的size是默认的还不是默认的

源码如下:

 /**
     * 用于空实例的共享空数组实例
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    /**
     * 共享的空数组实例,用于默认大小的空实例。我们将其与EMPTY_ELEMENTDATA区别开来,
     * 以知道添加第一个元素时需要扩容多少。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    // 无参构造函数,构造一个初始容量为10的空列表
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    /**
     * 有参构造函数,下面的注释解析了第四点知识点
     *
     * 初始化的时候只是初始化了对象数组,没有初始化size大小。
     * set()方法插入元素的时候,会进行校验就会抛出异常
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
 

(4)ArrayList初始化的时候即使设置了初始容量大小,但是不会设置size的值, set会有一个index和size大小校验,如果index大于size会抛出异常,只能通过add方法改变size的值,初始容量只是改变了在初始数组对象加了一个没有设置容量大小,但会设置一个默认的EMPTY_ELEMENTDATA或者DEFAULTCAPACITY_EMPTY_ELEMENTDATA空对象数组。 源码如下:

   public E set(int index, E element) {
        // 下标检查
        rangeCheck(index);

        // index下标位置上的原元素
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
    
    // 下标检查
     private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

     注意:size只有在ArrayList调用了add方法的时候才会增加size变量的值,rangeCheck方法校验size和index大小校验,index不能大于size,否则会抛出异常IndexOutOfBoundsException

posted @ 2020-01-27 19:57  壹尘  阅读(159)  评论(0编辑  收藏  举报