ArrayList源码学习第二天全解

目录

1,删除元素

1.1,删除单个元素remove

1.2, 移除多个元素removeRange

1.3,移除在 c 中的元素batchRemove

4,移除不在 c 中的元素retainAll

2,查找元素

2.1查找单个元素indexOf

2.2,获取指定位置的元素get

2.3,设置指定位置的元素set

2.4,转换成数组toArray() 

2.5求哈希值hashCode()

2.6, 判断2个list是否相等equals

3,清空元素clear()

4,序列化数组writeObject(java.io.ObjectOutputStream s)

5,反序列化数组readObject(java.io.ObjectInputStream s)

6,克隆clone() 

7,创建子数组subList(int fromIndex, int toIndex)

8, 创建 Iterator 迭代器iterator()

9,创建 ListIterator 迭代器listIterator(...)

10,foreach遍历 forEach(Consumer action)



1,删除元素

1.1,删除单个元素remove

remove(int index) 方法,移除指定位置的元素,并返回该位置的原元素。代码如下:

public E remove(int index) {
        rangeCheck(index);                       // 校验 index 不要超过 size

        modCount++;                             //增量++
        E oldValue = elementData(index);        //将index对应的元素赋值给 oldValue

        int numMoved = size - index - 1;           //计算集合需要移动元素个数
        if (numMoved > 0)                         //如果需要移动元素个数大于0,就使用arrayCopy方法进行拷贝  注意:数据源和数据目的就是elementData
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work      //将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收

        return oldValue;                                                     //返回被删除的元素
    }

1.2, 移除多个元素removeRange

removeRange(int fromIndex, int toIndex) 方法批量移除 [fromIndex, toIndex) 的多个元素,注意不包括 toIndex 的元素噢。代码如下:

  protected void removeRange(int fromIndex, int toIndex) {
        modCount++;                                              //增量++
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

    测试(因为这个测试比较特殊Test02类继承了ArrayList)

 @Test
    public void test02(){
        Test02 list = new Test02();
        list.add("司理理");
        list.add("苏小小");
        list.add("李师师");
        list.add("李师师1");
        list.add("苏小小1");
        list.add("司理理1");
        list.removeRange(0,4);
        System.out.println(list);
    }

1.3,移除在 c 中的元素batchRemove

removeAll(Collection<?> c) 方法批量移除指定的多个元素。实现逻辑比较简单,但是看起来会比较绕当然看代码也会有点绕绕,嘿嘿。代码如下:

 public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);              //检验是否为空空指针
        return batchRemove(c, false);
    }

调用 batchRemove(Collection<?> c, boolean complement, final int from, final int end) 方法,批量移除指定的多个元素。代码如下

private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {       // <1> 优化,顺序遍历 elementData 数组,找到第一个不符合 complement ,然后结束遍历。
            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;
    }

complement 参数,翻译过来是“补足”的意思。怎么理解呢?表示如果 elementData 元素在 c 集合中时,是否保留。

  • 如果 complement 为 false 时,表示在集合中,就不保留,这显然符合 #removeAll(Collection<?> c) 方法要移除的意图。
  • 如果 complement 为 true 时,表示在集合中,就暴露,这符合我们后面会看到的 #retainAll(Collection<?> c) 方法要求交集的意图。

4,移除不在 c 中的元素retainAll

 retainAll(Collection<?> c) 方法,求 elementData 数组和指定多个元素的交集。简单来说,恰好和 removeAll(Collection<?> c) 相反,移除不在 c 中的元素。代码如下:

public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);            //判断传进来的集合是不是为空
        return batchRemove(c, true);
    }

也调用了 batchRemove(Collection<?> c, boolean complement, final int from, final int end) 方法,批量移除指定的多个元素

2,查找元素

2.1查找单个元素indexOf

indexOf(Object o) 方法,查找首个为指定元素的位置。代码如下:非常简单

 public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

 contains(Object o) 包含方法,就是基于该方法实现。代码如下:

 public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

有时我们需要查找最后一个为指定元素的位置,所以会使用到lastIndexOf(Object o) 方法。代码如下:和上面对比就是循环一个从前面比对一个从后面比对

public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

2.2,获取指定位置的元素get

get(int index) 方法,获得指定位置的元素。代码如下:

elementData(int index)

public E get(int index) {
        rangeCheck(index);        //校验下标是否超出

        return elementData(index);
    }


E elementData(int index) {
        return (E) elementData[index];
    }

2.3,设置指定位置的元素set

set(int index, E element) 方法,设置指定位置的元素。代码如下:

 public E set(int index, E element) {
        rangeCheck(index);          //校验是否下标越界

        E oldValue = elementData(index);  //.要修改的元素
        elementData[index] = element;       //将修改元素给要修改的位置
        return oldValue;                    //将修改钱元素返回
    }

2.4,转换成数组toArray() 

toArray() 方法,将 ArrayList 转换成 [] 数组。代码如下:

public Object[] toArray() {
        //就是复制elementData数组,size是复制的长度
        return Arrays.copyOf(elementData, size);   
    }


    //CLASS类里的
    public native Class<?> getComponentType();

Arrays.copyof

 //这个方法就是取出传入数组的class对象作为第三个参数调用适用性更广的另一个函数而已.
 public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }



 public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]>newType{
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

        首先这个方法有两个泛型,一个是T,一个是U,T代表传入的第三个个参数newType(传入的类型的class对象),U 代表传入的第一个对象original(即原数组的数据类型)
       三元运算符的判断条件**((Object)newType == (Object)Object[].class)是用来判断newType      
是否是Object类型,在上述第一个方法的前提下代表判断原数组是否为Object类型的数组. 
       (Object)newType**这里为什么要转型是因为不转型会报错
      如果判断为真,即原数组是Object类型数组,则直接创建一个给定长度newLength的新的Object数组即可.(为什么要创建新数组呢?因为这个函数目的就是复制一个数组的指定部分到一个新数组.) 
    如果判断为假,即原数组不是Object类型数组,则调用**(T[])         
    Array.newInstance(newType.getComponentType(), newLength)**这个方法是新建一个数组,数组类型为newType中元素的类型(默认返回Object类型,可强制转换为正确类型,详情看下列代码例),长度为 指定的newLength.

getComponentType()在class类里面本地方法,返回数组内的元素类型,不是数组时,返回null

public native Class<?> getComponentType();
System.arraycopy
public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

2.5求哈希值hashCode()

hashCode() 方法,求 ArrayList 的哈希值。代码如下:

public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

Object类理

public native int hashCode();

选择31的原因看看String的hashCode方法是怎样实现的

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

2.6, 判断2个list是否相等equals

equals(Object o) 方法,判断是否相等。代码如下:

public boolean equals(Object o) {
        if (o == this)                      // 如果是自己,直接返回相等
            return true;
        if (!(o instanceof List))           // 如果不为 List 类型,直接不相等
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

    可能第一眼让胖友比较费解的是,为什么根据类型是否为 ArrayList ,调用了两个不同的方法去比对呢?因为普通的 List ,我们只能使用 Iterator 进行迭代,相比 ArrayList 的 elementData 属性遍历,性能会略低一些。处处是细节哈

3,清空元素clear()

clear() 方法,清空数组。代码如下:

 public void clear() {
        modCount++;            // 获得当前的数组修改次数

        // clear to let GC do its work    // 遍历数组,倒序设置为 null
        for (int i = 0; i < size; i++)        
            elementData[i] = null;

        size = 0;
    }

4,序列化数组writeObject(java.io.ObjectOutputStream s)

writeObject(java.io.ObjectOutputStream s) 方法,实现 ArrayList 的序列化。代码如下:

 private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff// 获得当前的数组修改次数
        int expectedModCount = modCount;
         // <1> 写入非静态属性、非 transient 属性
        s.defaultWriteObject();    

        // Write out size as capacity for behavioural compatibility with clone()
        // 写入 size ,主要为了与 clone 方法的兼容
        s.writeInt(size);       

        // Write out all elements in the proper order.  3> 逐个写入 elementData 数组的元素
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
        // 如果 other 修改次数发生改变,则抛出 ConcurrentModificationException 异常
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

5,反序列化数组readObject(java.io.ObjectInputStream s)

readObject(java.io.ObjectInputStream s) 方法,反序列化数组。代码如下:

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff读取非静态属性、非 transient 属性
        s.defaultReadObject();

        // Read in capacity
        s.readInt(); // ignored 读取 size ,不过忽略不用

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.逐个读取
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

6,克隆clone() 

clone() 方法,克隆 ArrayList 对象。代码如下:

public Object clone() {
        try {       // 调用父类,进行克隆
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size); // 拷贝一个新的数组
            v.modCount = 0;                                     // 设置数组修改次数为 0
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

7,创建子数组subList(int fromIndex, int toIndex)

subList(int fromIndex, int toIndex) 方法,创建 ArrayList 的子数组。代码如下:

SubList 不是一个只读数组,而是和根数组 root 共享相同的 elementData 数组,只是说限制了 [fromIndex, toIndex) 的范围

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

    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

8, 创建 Iterator 迭代器iterator()

iterator() 方法,创建迭代器。一般情况下,我们使用迭代器遍历 ArrayList、LinkedList 等等 List 的实现类。代码如下:

创建 Itr 迭代器。Itr 实现 ListIterator 接口,是 ArrayList 的内部类虽然说 AbstractList 也提供了一个 Itr 的实现,但是 ArrayList 为了更好的性能,所以自己实现了,在其类上也有注释“An optimized version of AbstractList.Itr”。

 public Iterator<E> iterator() {
        return new Itr();
    }



private class Itr implements Iterator<E> {
     index of next element to return下一个访问元素的位置,从下标 0 开始。
        int cursor;       
    // index of last element returned; -1 if no such上一次访问元素的位置。  初始化为 -1 ,表示            
    //无上一个访问的元素    遍历到下一个元素时,lastRet 会指向当前元素,而 cursor 会指向下一个元 
    // 素。这样,如果我们要实现 remove 方法,移除当前元素,就可以实现了。  移除元素时,设置为 -1 
    //,表示最后访问的元素不存在了,都被移除咧。
        int lastRet = -1; 
        int expectedModCount = modCount;        //创建迭代器时,数组修改次数。

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

下面,让我们来看看 Itr 对 Iterator 的 4 个实现方法。

hasNext() 方法,判断是否还可以继续迭代。代码如下

public boolean hasNext() {
       //cursor 如果等于 size ,说明已经到数组末尾,无法继续迭代了。
        return cursor != SubList.this.size;
   }

next() 方法,下一个元素。代码如下

public E next() {
      // 校验是否数组发生了变化
         checkForComodification(); 
        //i 记录当前 cursor 的位置
        int i = cursor;  
         // 判断如果超过 size 范围,抛出 NoSuchElementException 异常         
        if (i >= SubList.this.size) 
        throw new NoSuchElementException();
      Object[] elementData = ArrayList.this.elementData;
  //判断如果超过 elementData 大小,说明可能被修改了,抛出 ConcurrentModificationException 异常
       if (offset + i >= elementData.length)   
      throw new ConcurrentModificationException();
       //cursor 指向下一个位置
      cursor = i + 1;       
     //返回当前位置的元素  
    return (E) elementData[offset + (lastRet = i)];    
                }

remove() 方法,移除当前元素。代码如下:

public void remove() {
 // 如果 lastRet 小于 0 ,说明没有指向任何元素,抛出 IllegalStateException 异常
       if (lastRet < 0)   
          throw new IllegalStateException();
             // 校验是否数组发生了变化
           checkForComodification();  
              //移除 lastRet 位置的元素 
              try {          
                  SubList.this.remove(lastRet);
            //cursor 指向 lastRet 位置,因为被移了,所以需要后退下
             cursor = lastRet;  
                 //lastRet 标记为 -1 ,因为当前元素被移除了     
               lastRet = -1;   
                //记录新的数组的修改次数       
                  expectedModCount = ArrayList.this.modCount; 
               } catch (IndexOutOfBoundsException ex) {
                  throw new ConcurrentModificationException();
                 }
                }

forEachRemaining(Consumer<? super E> action) 方法,消费剩余未迭代的元素。代码如下

public void forEachRemaining(Consumer<? super E> action) {
            int i, hi, mc; // hoist accesses and checks from loop
            ArrayList<E> lst; Object[] a;
            if (action == null)
                throw new NullPointerException();
            if ((lst = list) != null && (a = lst.elementData) != null) {
                if ((hi = fence) < 0) {
                    mc = lst.modCount;
                    hi = lst.size;
                }
                else
                    mc = expectedModCount;
                if ((i = index) >= 0 && (index = hi) <= a.length) {
                    for (; i < hi; ++i) {
                        @SuppressWarnings("unchecked") E e = (E) a[i];
                        action.accept(e);
                    }
                    if (lst.modCount == mc)
                        return;
                }
            }
            throw new ConcurrentModificationException();
        }

9,创建 ListIterator 迭代器listIterator(...)

listIterator(...) 方法,创建 ListIterator 迭代器。代码如下:

//这个方法可以选择多少个元素去迭代
 public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }


//没有参数的
public ListIterator<E> listIterator() {
        return new ListItr(0);
    }

创建 ListItr 迭代器。ListItr 实现 了ListIterator 接口,是 ArrayList 的内部类。虽然说 AbstractList 也提供了一个 ListItr 的实现,但是为了ArrayList的 性能,所以自己实现了,在其类上也有注释“An optimized version of AbstractList.ListItr”。

 private class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            super();
            cursor = index;
        }

        public boolean hasPrevious() {
            return cursor != 0;
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor - 1;
        }

        @SuppressWarnings("unchecked")
        public E previous() {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[lastRet = i];
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

10,foreach遍历 forEach(Consumer<? super E> action)

 forEach(Consumer<? super E> action)循环遍历代码如下:

 public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ehdjsbs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值