ArrayList源码分析二

ArrayList源码分析

ArrayList有三个构造方法:

 1.无参构造方法

  public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ArrayList中储存数据的其实就是一个数组,这个数组就是elementData,默认大小是10. 

public class ArrayLiat {
    //默认空容量的数组,长度为0
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //集合真正存储数据的容器
    Object[] elementData;

    //空参构造
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
}

2.有参构造函数一

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

 3.有参构造函数二

   public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main8);
        ArrayList list  =new ArrayList();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        ArrayList<String> arrayList = new ArrayList(list);
        for (String s : arrayList){
            System.out.println(s);
        }
    }

 解析addAll()方法

   public boolean addAll(int index, Collection<? extends E> c) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        //移动
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
        //复制
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
/**
 * src:源数组
 * srcPos:原数组中的起始位置
 *dest:目的数组
 *destPos:目的数组中的起始位置
 *length:要复制的数组元素的数量
 */
arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

案例:集合底层元素时如何移动的 

/**
 * a: 数据源
 * 0:从0索引开始复制
 * elementData:复制到elementData数组中
 * index:复制到那个地方
 * numNex:复制几个
 */
 System.arraycopy(a, 0, elementData, index, numNew);
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main9);
        //数据源
        String[] sourceList ={"apple","banana","orange"};
        //数据目的
        String[] targetList = {"mango","peach",null,null,null,null,null,null,null,null};

        int numNew = sourceList.length;
        //numMoved:集合的真实长度 - 要存的索引位置 要移动的元素的个数
        int numMoved = 2 - 1;

        if (numMoved > 0){
            System.arraycopy(targetList, 1, targetList, 1 + numNew, numMoved);
        }
        System.out.println(Arrays.toString(targetList));
    }

完成了移动的过程。

解析toString()方法

public String toString() {
        //获取迭代器
        Iterator<E> it = iterator();
        //判断迭代器是否有元素
        if (! it.hasNext())
            return "[]";
        
        //创建StringBuilder
        StringBuilder sb = new StringBuilder();
        //先追加了'['
        sb.append('[');
        //无限循环
        for (;;) {
            //调用迭代器的next方法取出元素,且光标向下移动
            E e = it.next();
            //三元判断
            sb.append(e == this ? "(this Collection)" : e);
            //没有元素啦!!!,追加']',结束循环
            if (! it.hasNext())
                return sb.append(']').toString();
           //追加了',' 和' '
            sb.append(',').append(' ');
        }
    }

迭代器

案例一:使用迭代器遍历集合里面的元素。

    ArrayList list  =new ArrayList();
    list.add("aaa");
    list.add("bbb");
    list.add("ccc");
    
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()){
    String s = iterator.next();
    System.out.println(s);
    }

    //获取迭代器的方法
    public Iterator<E> iterator() {
        //创建了一个对象
        return new Itr();
    }
   //ArrayList集合的内部类 -->迭代器的源码
    private class Itr implements Iterator<E> {

        protected int limit = ArrayList.this.size;

        int cursor;       // 光标,默认值是0
        int lastRet = -1; // 记录
        //将集合实际修改次数,赋值给期望修改次数
        int expectedModCount = modCount;
        
        //判断集合是否有元素
        public boolean hasNext() {
            return cursor < limit;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            //预期修改次数和实际修改次数是否一样
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            //将光标赋值给i
            int i = cursor;
            //判断,如果大于集合的长度就说明没有元素了
            if (i >= limit)
                throw new NoSuchElementException();

           //把集合存储数据的数组赋值给该方法的局部变量 
            Object[] elementData = ArrayList.this.elementData;
            //进行判断,如果条件满足就会抛出并发修改异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            //光标要自增,向下移动
            cursor = i + 1;
            //从数组中取出元素返回
            return (E) elementData[lastRet = i];
        }

    }

 案例二:已知集合:ArrayList list =new ArrayList();里面有三个元素,“Android”,“Java”,“PHP”,使用迭代器遍历有没有   “Java”元素。如果有就删除该元素。

ArrayList list  =new ArrayList();
  list.add("Android");
  list.add("Java");
  list.add("PHP");

  Iterator<String> iterator = list.iterator();
  while (iterator.hasNext()){
  String s = iterator.next();
  if(s.equals("Java")){
  list.remove("Java");
  }
  }

 注意:这里不可以使用list.remove("Java"),会报并发修改异常ConcurrentModificationException,我们可以去看下源码

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

我们添加了三个元素,集合实际修改的次数modCount为3

  //ArrayList集合的内部类 -->迭代器的源码
    private class Itr implements Iterator<E> {
        protected int limit = ArrayList.this.size;
        int cursor;       // 光标,默认值是0
        int lastRet = -1; // 记录
        //将集合实际修改次数,赋值给期望修改次数
        int expectedModCount = modCount;
}

获取迭代器的时候,那么expectedModCount 的值也是3

集合里面的remove方法

  public boolean remove(Object o) {
        //要删除的元素是否为null
        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;
    }

 集合实际修改次数为4,但是预期 修改次数是3

 //真正删除元素的方法
 private void fastRemove(int index) {
        //集合实际修改次数会自增
        modCount++;
        //计算集合要移动元素的个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //让删除的元素置为null,尽快让垃圾回收期
        elementData[--size] = null; // clear to let GC do its work
    }

所以在下一次的next的时候就会报异常

  @SuppressWarnings("unchecked")
        public E next() {
            //预期修改次数和实际修改次数是否一样
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
          
             //将光标赋值给i
            int i = cursor;
            //判断,如果大于集合的长度就说明没有元素了
            if (i >= limit)
                throw new NoSuchElementException();

           //把集合存储数据的数组赋值给该方法的局部变量 
            Object[] elementData = ArrayList.this.elementData;
            //进行判断,如果条件满足就会抛出并发修改异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            //光标要自增,向下移动
            cursor = i + 1;
            //从数组中取出元素返回
            return (E) elementData[lastRet = i];
        }

 结论:

一:集合每次调用add方法的时候,实际修改次数变量modCount 的值都会自增一次

二:在获取迭代器的时候,集合只会执行一次将实际修改集合的次数modCount赋值给预期修改集合的次数expectedModCount

三:集合在删除元素的时候,也会针对实际修改次数的变量modCount进行自增的操作

所以下面就会产生并发修改异常

 public E next() {
            //预期修改次数和实际修改次数是否一样
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
}

案例三:已知集合:ArrayList list =new ArrayList();里面有三个元素,“Android”,“Java”,“PHP”,使用迭代器遍历有没有“PHP”元素。如果有就删除该元素。

    ArrayList list  =new ArrayList();
    list.add("Android");
    list.add("Java");
    list.add("PHP");

    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()){
    String s = iterator.next();
    if(s.equals("PHP")){
    list.remove("PHP");
    }
    }

运行结果:并不会发生并发修改异常

结论:当要删除的元素时集合最后一个元素的时候不会发生并发修改异常

原因:在调用hasNext的时候,光标的值等于集合的长度,那么就会返回false

因此就不会调用next方法获取集合的元素,所以就不会发生并发修改异常。

  public boolean hasNext() {
            return cursor < limit;
        }

迭代器中的remove方法,删除集合中的元素

    ArrayList list  =new ArrayList();
    list.add("Android");
    list.add("Java");
    list.add("PHP");

    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()){
    String s = iterator.next();
    if(s.equals("PHP")){
    iterator.remove();
    }
    }

删除集合元素remove方法解析

 public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

            try {
                //使用集合的remove方法删除元素,内部类调用外部类的方法
                //实际修改数加1,集合数量减1
                ArrayList.this.remove(lastRet);
                
                cursor = lastRet;
                lastRet = -1;
                //把实际修改次数赋值给预期修改次数
                expectedModCount = modCount;
                limit--;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

结论:

一:迭代器调用remove方法删除元素,底层还是调用集合自己的remove方法删除元素

二:在调用remove方法中每次都会给预期修改数赋值

清空集合元素方法clear()解析

   public void clear() {
        //实际修改次数自增
        modCount++;

        // 循环遍历集合,让数组里面的元素置null,让垃圾回收器尽早回收
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        //集合的长度置为0
        size = 0;
    }

集合元素方法contains()解析

    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
    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;
    }

猜你喜欢

转载自blog.csdn.net/jingerlovexiaojie/article/details/109047271