Java集合源码分析(一)——ArrayList源码分析

一、如何看源码:

  • 看数据结构:底层数据结构
  • 看继承结构:类的层次结构,处于一个什么样的位置
  • 看构造方法:看看做了哪些事,跟踪方法里面的方法
  • 看常用的方法:与构造方法类型,看看该方法是如何实现的

二、ArrayList源码分析:

1、数据结构:

ArrayList是基于数组,数组元素类型为Object,即可以存放所有类型的数据

2、继承结构:

Java集合源码分析(一)——ArrayList源码分析_第1张图片

问题1:为什么要用AbstractList实现List,然后ArrayList继承AbstractList呢

答:由于接口中包含的都是抽象方法(JDK8之前),而抽象类中可以包含抽象方法还可以包含具体实现的方法,所以可以用AbstractList实现List中通用的方法,然后ArrayList实现自己特有的方法,就能让代码更简洁,减少重复代码

问题2:为什么AbstractList实现了List,ArrayList也实现了List,据源码作者说,这其实是一个mistake,因为他写这个代码的时候觉得会有用,实际上没有用,但也没什么影响,就没删除

问题3:ArrayLisr实现了哪些接口:

  • List接口
  • RandomAccess接口:这是一个标记性接口,用于快速随机存取,实现了该接口时,表明使用for循环效率更高;而没有实现该接口,则表明用Iterator效率更高
  • Cloneable接口:实现了该接口,就可以用Object.clone()方法
  • Serializable接口:实现了该接口,表明该类可以被序列化

3、成员变量

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;
	 
	 //实际元素大小
	 private int size;
	 
	 //Integer.MAX_VALUE = 0x7fffffff,因为有些VMs需要保留一些头信息在一个数组里面,所以-8
	 //最大数组容量
	 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

4、构造方法(3个)

  • 无参构造方法:将elementData初始化为一个默认容量为10的空数组对象,为什么会是默认容量为10,看newCapacity方法
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 指定初始容量的构造方法:将elementData初始化为一个指定容量的数组对象,若指定容量为0,则将elementData初始化为空数组对象(EMPTY_ELEMENTDATA)
   /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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 a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

问题1:为什么要判断"elementData.getClass() != Object[].class" 

答:每个集合的toArray()实现方法不一样,所以需要去判断,若不是Object.class(),则需要用ArrayList中方法去改造一下

总结:ArrayList的构造方法其实就是对elementData(存储数据的容器)进行初始化

5、常用方法

  • add()方法(有4个)
//在表尾添加元素
 public boolean add(E e){
	modCount++;
	add(e, elementData, size);
	return true;
}
//在指定位置添加元素
public void add(int index, E element){
	rangeCheckForAdd(index);
	modCount++;
	final int s;
	if((s = size) == (elementData = this.elementData).length)
	   elementData = grow();
	System.arraycopy(elementData, index, elementData, index+1, size - index);
	elementData[index] = element;
	size = s + 1;
}

 

//在末尾增加集合
	public boolean addAll(Collection c){
		Object[] a = c.toArray();
		modCount++;
		int numNew = a.length;
		if(numNew == 0)
		   return false;
		Object[] elementData;
		final int s;
		if(numNew > (elementData = this.elementData).length - (s = size))
		   elementData = grow(numNew + s);
		System.copyOf(a, 0, elementData, s, numNew);
		return true;
	}
//在指定位置处添加集合
public boolean addAll(int index, Collection c){
	rangeCheckForAdd(index);
	Object[] a = c.toArray();
	modCount++;
	int numNew = a.length;
	if(numNew == 0)
	    return false;
	Object[] elementData;
	final int s;
	if(numNew > (elementData = this.elementData).length - (s = size))
	    elementData = grow(s + numNew);
	int numMoved = s - index;
	if(numMoved > 0)
	    System.copyOf(elementData, index, elementData, index + numMoved, numMoved);
	System.copyOf(a, 0, elementData, index, numNew);
	size = s + numNew;
	return true;
}

第一个add方法中调用了ArrayList内部方法:

private void add(E e, Object[] elementData, int s){
	if(s == elementData.length)
	    elementData = grow();
	elementData[s] = e;
	size = s + 1;
}
  • 扩容方法
private Object[] grow(int minCapacity) {
       return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));
}

newCapacity():ArrayList核心代码,用于数组扩容

private int newCapacity(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) { //一般是elementData为空数组时才会出现这种情况
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // 溢出
                throw new OutOfMemoryError();
            return minCapacity;
        }
        //超出最大容量限制,则会调用hugeCapacity方法
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

hugeCapacity():用于赋予最大值,当容量超出最大值限制时会调用 

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // 溢出
            throw new OutOfMemoryError();
        //又比较了一遍,双重保护
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
 }
  • 删除

remove(int index):删除指定位置处元素,并返回删除值

 public E remove(int index) {
        Objects.checkIndex(index, size);
        final Object[] es = elementData;

        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
        fastRemove(es, index);

        return oldValue;
 }

remove(Object o):删除指定元素

 public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;
        found: {
            if (o == null) {
                for (; i < size; i++)
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    if (o.equals(es[i]))
                        break found;
            }
            return false;
        }
        fastRemove(es, i);
        return true;
 }

fastRemove:移动元素

private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
}

removeAll(Collection c):删除与集合c的交集

public boolean removeAll(Collection c) {
        return batchRemove(c, false, 0, size);
}

retainAll(Collection c):保留与集合c的交集,即删除不包含在集合c中的元素

public boolean retainAll(Collection c) {
        return batchRemove(c, true, 0, size);
}

 batchRemove()方法:若传入的complement为false,则用于removeAll(),若传入的complement为true,则用于retainAll()

 boolean batchRemove(Collection c, boolean complement,
                        final int from, final int end) {
        Objects.requireNonNull(c);
        final Object[] es = elementData;
        int r;  //用来控制循环
        for (r = from;; r++) {
            if (r == end)
                return false;
            if (c.contains(es[r]) != complement)  //用来寻找第一个等于(或不等于)集合元素
                break;
        }
        int w = r++;  //用来记录有多少个
        try {
            for (Object e; r < end; r++)
                if (c.contains(e = es[r]) == complement)
                    es[w++] = e;
        } catch (Throwable ex) {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            System.arraycopy(es, r, es, w, end - r);
            w += end - r;
            throw ex;
        } finally {
            modCount += end - w;
            shiftTailOverGap(es, w, end);
        }
        return true;
    }

清空

public void clear() {
        modCount++;
        final Object[] es = elementData;
        for (int to = size, i = size = 0; i < to; i++)
            es[i] = null;
 }
  • 获取指定元素的索引
public int indexOf(Object o) {
        return indexOfRange(o, 0, size);
    }

int indexOfRange(Object o, int start, int end) {
        Object[] es = elementData;
        if (o == null) {
            for (int i = start; i < end; i++) {
                if (es[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = start; i < end; i++) {
                if (o.equals(es[i])) {
                    return i;
                }
            }
        }
        return -1;
}
  • 重写hashCode方法
public int hashCode() {
        int expectedModCount = modCount;
        int hash = hashCodeRange(0, size);
        checkForComodification(expectedModCount);
        return hash;
 }

 int hashCodeRange(int from, int to) {
        final Object[] es = elementData;
        if (to > es.length) {
            throw new ConcurrentModificationException();
        }
        int hashCode = 1;
        for (int i = from; i < to; i++) {
            Object e = es[i];
            hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
        }
        return hashCode;
 }

6、总结

  • ArrayList可以存放null元素
  • ArrayList本质上就是一个Object数组elementData
  • ArrayList区别于数组的地方在于能自动扩展大小,实现方法是grow()以及newCapacity()
  •  arrayList由于本质是数组,所以它在数据的查询方面会很快,而在插入删除这些方面,性能下降很多
  • arrayList实现了RandomAccess,所以在遍历它的时候推荐使用for循环。

 

 

 

 

 

你可能感兴趣的:(Java集合源码分析(一)——ArrayList源码分析)