Java集合之ArrayList

134 阅读3分钟

ArrayList我们已经不再陌生了,都已经学会使用了。但是,就是在你面试的时候,面试官问出来的问题,但你回答不上来,你能怎么办?你很想哭啊。我明明平常都会用了,为啥一问三不知。那是因为你会的只是最基础的,只会使用工具,没有了解工具的原理。我来划重点,记住下面的知识点,不用再怕面试官了。

1、为什么要定义两个空数组EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA? 这个问题,其实ArrayList.Java文档的注释中就有答案哈:

     /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
     /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    // Android-note: Also accessed from java.util.Collections
    transient Object[] elementData; 
    // non-private to simplify nested class access

还是看不懂?有道翻译一下啦:

用于默认大小的空实例的共享空数组实例。我们将其与EMPTY_ELEMENTDATA区分开来,
以了解添加第一个元素时需要膨胀多少。
存储ArrayList元素的数组缓冲区。ArrayList的容量是这个数组缓冲区的长度。
任何空ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
当添加第一个元素时,将扩展为DEFAULT_CAPACITY。

还是有点蒙圈哈,再看下构造方法和扩充数组的方法就ok了啦。

     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);
       }
   }

   /**
    * Constructs an empty list with an initial capacity of ten.
    */
   public ArrayList() {
       this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
   }
   
   
   public void ensureCapacity(int minCapacity) {
       int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
           // any size if not default element table
           ? 0
           // larger than default for default empty table. It's already
           // supposed to be at default size.
           : DEFAULT_CAPACITY;

       if (minCapacity > minExpand) {
           ensureExplicitCapacity(minCapacity);
       }

明白了吧。如果我们初始化的时候,使用了有参数的构造方法,如果传进来的参数为0,那么就赋值为EMPTY_ELEMENTDATA,如果大于0,那就new 一个数组就好了。但如果使用了无参数的构造方法,那就默认赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。这样区分的目的是,在第一次扩充数组的时候,扩充的最小值不一样。前者是0后者是,默认值10. 那么问题又来了,我们应该使用哪种方式初始化最优呢?换句话说,哪种方式初始化,更省内存呢?ArrayList(),还是ArrayList(0)呢?我觉得是ArrayList(0)。

2、modCount变量在整个过程中有什么作用?

  if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

这个就是防止在迭代的过程中集合被修改。。

3、ArrayList有几个内部类,有什么作用?

Itr,ListItr,SubList.

Itr,迭代器的实现类。

ListItr,迭代器集合

SubList,子集合

4、transient这个修饰符的作用? transient:修饰符 使用对象:字段

作用:用来标识一个成员变量在序列化子系统中应被忽略。

就是一个不想让某字段在序列化后,别人反序列化时,拿到这个字段的值。添加了这个属性,通过网络传输后,反序列时,这个字段是为null的。

5、ArrayList是如何扩充数组容量的?

找到对应方法:

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

首先新的容量值为旧容量值加上旧容量值的一半,也就是原来的1.5倍。再比较需要扩充的最小容量谁大谁小,取最大的那个。