ArrayList部分源码的理解

集合与数组的区别

在我们学习 JavaEE 的时候是先学习数组,然后再学习集合,那么我们先看看数组与集合有什么区别;

1、数组是一个线性序列,创建完成之后的容量是不可变得,并且生命周期也是不能改变,可以通过索引获取元素,如果如果发现有越界现象,会报 java.lang.ArrayIndexOutOfBoundsException 异常错误,检查边界会以效率为代价,集合是动态扩容(有点类似于String、String Buffer的概念);

2、数组的存放的类型只能是一种(基本类型/引用类型),集合(这里指双列集合)存放的类型可以不是一种(不加泛型时添加的类型是Object));

注意:ArrayList只能存储对象类型;

ArrayList部分源码的理解

本文将从以下三个点理解ArrayList,如有不当之处,望大佬指点:

1、成员变量的含义以及构造方法;

2、从add()中理解扩容机制;

3、常用的Api;

4、ArrayList的遍历方式;

1、成员变量的含义以及构造方法

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
   
   // 序列化id
   private static final long serialVersionUID = 8683452581122892189L;
   
   //默认的初始化空间
   private static final int DEFAULT_CAPACITY = 10;   
   
   //空的数组用于空对象初始化
   private static final Object[] EMPTY_ELEMENTDATA = {};

   private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

   //存储数组,非私有简化了嵌套类访问
   transient Object[] elementData; // non-private to simplify nested class access

   // 当前数组长度
   private int size;

   //有参构造方法
   public ArrayList(int initialCapacity) {
       //大于0就构造对应长度的Object数组
       if (initialCapacity > 0) {
           this.elementData = new Object[initialCapacity];
           //等于0就直接赋值空的数组对象
       } else if (initialCapacity == 0) {
           this.elementData = EMPTY_ELEMENTDATA;
           //小于0就抛出异常
       } else {
           throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
       }
   }

   //无参构造方法
   public ArrayList() {
       //直接赋值空的数组对象
       this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
   }

   //形参为集合的构造方法
   public ArrayList(Collection<? extends E> c) {
       //参数c为实现了Collection的类,toArray为Collection接口定义方法
       elementData = c.toArray();
       //判断数组的长度是否大于0   注意:数组.length   集合.size()
       if ((size = elementData.length) != 0) {
           // 判断elementData的类型是否是Object[],因为Arrays.copyOf返回类型依赖于第一个参数的类型
           // 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;
       }
   }
  //省略其它方法...
}

建议阅读下面链接文章:
https://blog.csdn.net/weixin_39452731/article/details/100189934
复制代码

2、Add( )

 //将指定的元素追加到此列表的末尾
     public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
         //ArrayList添加元素的实质是给数组赋值
        elementData[size++] = e;
         //永远返回True
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        //判断对象是否等于默认的空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //在默认的容量与对象实际容量之间去最大指
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //扩容
        ensureExplicitCapacity(minCapacity);
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //实际扩容方法
            grow(minCapacity);
    }
    
   private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
       //原始长度 + (原始长度右移一位)  ==  原始长度 + (原始长度 / 2)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
       //复制成一个新的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }       
    
    建议阅读下面链接文章:
    https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/ArrayList-Grow.md
复制代码

3、Remove()

public E remove(int index) {
        //长度检查
        rangeCheck(index);
        modCount++;
        //将指定位置(index)上的元素保存到oldValue
        E oldValue = elementData(index);
        //数组长度减一
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //将指定位置(index)上的元素都往前移动一位
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        //将最后面的一个元素置空,好让垃圾回收器回收
        elementData[--size] = null; // clear to let GC do its work
        //将原来的值oldValue返回
        return oldValue;
    }

	//检查长度是否合法
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
复制代码

4、Contains()

    public boolean contains(Object o) {
        //判断元素所在的索引是否大于0 
        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;
    }
复制代码

5、Clear()

    public void clear() {
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            //给每个元素赋值为null,方便垃圾回收
            elementData[i] = null;
		//集合的长度赋值为0
        size = 0;
    }
复制代码

6、equals() (父类AbstractList的方法)

    public boolean equals(Object o) {
    	//判断内存地址
        if (o == this)
            return true;
        //判断是否是List接口的实现类
        if (!(o instanceof 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());
    }
复制代码

7、 ArrayList的遍历方式

1、普通for循环遍历;
 for(int i = 0 ; i < list.size() ; i++){&emsp;
   system.out.println(list.get(i));&emsp;
}

2、增强for循环遍历;
for(String string:list){&emsp;
   system.out.println(string);&emsp;
}

3、Iterator迭代器遍历
 Iterator it = list.iterator();
&emsp; while(it.hasNext()) {
&emsp; System.out.println(it.next());
}  
复制代码

在此感谢大佬以及队友的帮助,在工作中得到了成长,小生不才,如有不当之处,望各位大佬指点一二!

猜你喜欢

转载自juejin.im/post/5e8f6194f265da47aa3f62da
今日推荐