Java源码学习之List子类:ArrayList

 List的子类:

      ArrayList:

  public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
         允许包括null的所有元素。除了非同步外,该类与Vector相同。
         在增加大量元素前通过ensureCapacity操作来增加ArrayList的操作也许能减少重分配的数量。

         切记此类是非同步的,如果多个线程同时访问ArrayList的实例且至少有一个在结构上改变了它,那它必须在外部被同步。这一般都是通过对一些自然“包裹”着list的对象进行同步来完成的。如果没有这样的对象,list应当使用Collections.synchronizedList来被“包裹”:

      List list = Collections.synchronizedList(new ArrayList(...));

         这应当在被创造的时候就完成,以免意外的非同步操作获取该list。

         当该类在结构上发生改变时,通过类的iterator()和listIterator(int)返回的迭代器会“立刻出错”(fast-fail),除非这个改变由迭代器自己的ListIterator.remove()或ListIterator.add(object)导致的。因此,当一个ArrayList同时面临修改时,迭代器将立刻且彻底地出错,而非在未来某个不确定的时间产生随机且不确定的风险。

         切记,一般而言迭代器的立刻出错行为是不能被保证的,因为当非同步的同时修改发生时制作强大的保证是不可能的。因此不能通过这个异常编写一个正确的程序,它仅能用来检测bug.

          ArrayList有4个有意思的字段:

   private static final int DEFAULT_CAPACITY = 10;    // 默认的初始化容量
   private static final Object[] EMPTY_ELEMENTDATA = {};    // 用于空实例的共享空数组
   private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};    // 用于默认大小空实例的共享空数组
   transient Object[] elementData;    // 存储ArrayList元素的缓存数组, ArrayList的容量即elementData的长度,
                                      // 空ArrayList的容量将在第一个元素被添加后被扩展到DEFAULT_CAPACITY  
            ArrayList有3个构造器:
    public ArrayList(int initialCapacity);    // 当iC == 0时,将EMPTY_ELEMENTDATA赋给elementData, iC > 0时开辟iC大小的Object数组给elementData(此时容量被确定了,但内容是空的),否则抛出异常。
    public ArrayList();    // 將DEFAULTCAPACITY_EMPTY_ELEMENTDATA賦给elementData,此时内容为空,容量为10
    public ArrayList(Collection<? extends E> c);    // 将c的元素数赋给size,若size == 0将EMPTY_ELEMENTDATA赋给elementData,否则将c.toArray()赋给elementData

            public void trimToSize(): 最小化ArrayList占有的空间。利用Arrays.copyOf将elementData修剪为size大小,当size == 0时ELEMENTDATA = EMPTY_ELEMENTDTATA。

     public void ensureCapacity(int minCapacity): 确保ArrayList至少能包含minCapcity个元素。可能需要调用ensureExplicitCapacity(minCapacity)。

    private static int calculateCapacity(Object[] elementData, int minCapacity){
        if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

       似乎是想说明,当elementData指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,其容量不得小于DEFAULT_CAPACITY。  

   private void ensureCapcityInternal(int minCapacity){
        ensureExplicitCapacity(calculateCapacity(elementDATA, minCapacity));
   }
   private void ensureExplicitCapcity(int minCapcity){
       modCount++;
       if(minCapacity - elementData.length > 0)
           grow(minCapacity);
   }
    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);
    }
        取minCapacity = max(elementData.length*1.5, minCapacity), 将elementData的容量扩充为newCapacity大小,当minCapacity大于MAX_ARRAY_SIZE,即Integer.MAX_VALUE - 8时特殊处理。注意它的size没变。
            
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

            对grow()而言,如果所想要扩充的范围很大,hugeCapacity按照minCapacity确定到底给多大。

            public int indexOf(Object o): 返回第一个元素o的下标,无则返回-1。通过遍历elementData实现。            

            public boolean contains(Object o): 判断indexOf(o) >=0?实现。

            public int lastIndexOf(Object o): 相比indexOf(),只是将遍历顺序由0~size - 1改为size - 1~0;

    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
         通过创建一个新变量并通过super.clone()初始化,接着将elementData赋给v.elementData。注意clone()在Object.class中以native关键字修饰,说明其方法的实现在其他语言的文件中,因此其过程不得而知。super.clone()可以看成固定用法了,但在涉及深拷贝的时候可能需要以class.clone形式出现。
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

         通过Array.copyOf()分配新内存,大小根据size确定,而不是elementData.length。

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

          当参数a的尺寸过小时,开辟新内存返回,否则将elementDATA的size个值拷贝给a,并将第size个赋值为null,以此确定原list的大小。可参考:

public static void main(String[] args) {  
        String[] ss = {"0", "1", "2", "3", "4"};
        List<String> s2 = new ArrayList<>();
        s2.add("a");
        s2.add("b");
        String[] s3 = s2.toArray(ss);
        println(ss);
        println(s3);
    } 
    /// a b null 3 4
    /// a b null 3 4

    public static void println(String[] str) {  
        for(int i = 0; i < str.length; i++){
        	System.out.print(str[i] + " ");
        }
        System.out.println();
    }  

           public E get(int index): 先调用rangeCheck(index)检查index是否越界,再返回elementData的第index个元素。

           public E set(int index, E element): 同样先检查index是否越界,保存elementData的第index个元素,将其替换并返回。

          public boolean add(E e): 调用ensureCapacityInternal(size+1)检查是否越界,再将第size个元素赋值,size++,返回true。

    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

          rangeCheckForAdd(index)与rangeCheck(index)的差别在于后者仅在index>size时异常,而前者在index<0是同样会抛出异常(IndexOutOfBoundsException),但是在调用get(-1)时程序会抛出ArrayIndexOutOfBoundsException的异常。第四行意味着把elementData从index开始的size-index个数据往后移一位。

   public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

           类似于add()。

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
            注意判断相等的条件是o.equals(elementData[index]),而equals在object.class中定义为:
    public boolean equals(Object obj) {
        return (this == obj);
    }

            即他们的引用相等才为相等,所幸string和long将其重写过,故只要元素相同即可。但自己定义的类则不行。如:

    public class Practice {
	int var;
	public Practice(int v) {
		var = v;
	}
	public static void main(String[] args) {  
		List<Practice> lm = new ArrayList<>();
		lm.add(new Practice(0));
		lm.add(new Practice(1));
		Practice tmp = new Practice(2);
		lm.add(tmp);
		lm.add(null);
		System.out.println(lm.remove(new Practice(0)));
		System.out.println(lm.remove(tmp));
		System.out.println(lm.remove(null));
        }  
    }// false true true

            private void fastRemove(int index): 与public boolean remove(int index)类似,少了检查和返回。

            public void clear(): 将整个size的内容全都指向null,将size归0。

           public boolean adAll(Collection<? extends E> c): 先确保容量(size+c.toArray().length),调用System.arraycopy()将c的内容搬运至elementData的size处,改变size。注意其返回值的说明是“list因调用而改变则为true”,事实上返回c.toArray.length!=0。

         public boolean addAll(int index, Collection<? extends E> c): 先用rangeCheckForAdd(index)检查越界问题,检查容量。分两次搬运元素,更改size,返回。

          protected void removeRange(int fromIndex, int toIndex): 搬运,清除(元素指向null),更改size。

     public boolean removeAll(Collection<?> c): 调用Object.requireNull(c)检查c是否为null,是则抛出异常,返回batchRemove(c, false)。

          public boolean retainAll(Collection<?> c): 同上,将false改为true。

    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

          机智!用contains()==complement判断是remove还是retain。但是此步骤可能会抛出异常,所以在finally中判断r != size 的情况下将elementData中r之后的元素都前移留下,同时更改w的大小。之后将w之后的元素赋为null,更改size,返回。

          private void writeObject(java.io.ObjectOutputStream s): 将ArrayList的实例状态写到一个流里面,即将其序列化。

    public ListIterator<E> listIterator(int index){...}
    public ListIterator<E> listInteraor(){...}
    public Iterator<E> iterator(){...}
    public Spliterator<E> spliterator() {...}

         四种生成迭代器的方法。其中第三种返回了一个内部类Itr,其主要的方法是hasNext(),next(),remove()。前两者各返回了一个继承自Itr的内部类ListItr(index/0),它包含方法hasPrevious(), nextIndex(), previousIndex(),set(), add()。注意:若ArrayList内容为("1","2","3"),则next(),next(),previous()输出依次为"1","2","2"。

         第四种同样返回了一个内部类ArrayListSpliterator,它继承自Spliterator。它的特点在于在遍历过程中能检查到尽量多的干扰,而不牺牲效率,这主要依赖于modCount。虽然它不能保证检查到并发冲突,对于线程内的干扰也过于保守,但在使用中能检查到足够多的错误,这是值得的。为此,1.惰性初始化fence和expectedModCount;2.在forEach的结尾处只检查一次ConcurrentModificationException。不是很看得懂。

         private void readObject(java.io.ObjectInputStream s): 从一个流中重塑ArrayList实例,即反序列化。

    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

      注意返回list上的改变会导致原list的改变。SubList是ArrayList的一个内部类,可以参考其对外部类变量的引用(如:ArrayList.this.elementData[i]),其重写了set, get等诸多方法,实现方法类似,不赘述。

        public void forEach(Consumer<? super E> action): 实现代码不是很能看懂,其效果是将所有元素执行 action动作(但不会改变原有的元素值),除非有异常抛出则终止。在没有明确的规定下,其操作顺序同其迭代顺序。用法为.forEach(k->System.out.println(k));

        public boolean removeIf(Predicate<? super E> filter): 创建了一个BitSet存储对应位元素是否需删除。通过遍历元素更新BitSet。再依据BitSet更新elementData,更新size,modCount,返回。用法为.removeIf(k->k=="0");

         public void replaceAll(UnaryOperator<E> operator): 遍历所有元素,执行operate操作。用法为.replaceAll(k->k+"ok");

         public void sort(Comparator<? super E> c): 调用Array.sort((E[]) elementData, 0 , size, c)。其排序方法各不相同。粗略地看了一下有归并排序,这一部分当成算法学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值