ArrayList源码分析,来啦!!!

1、ArrayList的继承关系图

在这里插入图片描述
源码继承关系

public class ArrayList<E> extends AbstractList<E>
   implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  1. AbstractList抽象类:List的一个"骨架"实现
  2. RandomAccess:一种标记接口,实现了该接口,就表示该实现支持快速随机访问,(随机访问比顺序访问的效率要高)
  3. Cloneable:一种标记接口,实现改接口能够实现克隆,若不实现该接口,用Object.clone会报错
  4. Serializable:一种标记接口,实现改接口,他支持序列化,能够通过序列化进行传输

2、ArrayList源码分析

2.1 构造方法

(1)无参构造

public ArrayList() {
	// 赋值,把一个默认长度为0的空容量的数组赋值给集合真正存储数据的容器
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

(2)有参构造

// 创建指定大小的elementData
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();//1--2--3
    // 将数组的长度赋值给size,判断是否不等于0
    if ((size = elementData.length) != 0) {
    	// 再次进行判断
    	if (elementData.getClass() != Object[].class)
    		// 数组的创建与拷贝
			elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
      // 就把空数组的地址赋值给集合存元素的数组
		this.elementData = EMPTY_ELEMENTDATA;
    }
}


// 1
// 将集合转化为数组的方法
public Object[] toArray() {
	// 调用数组工具类的方法,将集合存元素的数组作为第一个参数,集合的长度作为第二个参数
	return Arrays.copyOf(elementData, size);
}

//2
class Arrays{
	public static <T> T[] copyOf(T[] original, int newLength) {
		// 再次调用方法得到数组
        return (T[]) copyOf(original, newLength, original.getClass());
    }


	// 3
	public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        // 通过三目运算判断两个数组,不管结果如何,都会创建一个新的数组
        // 新数组的长度一定和集合的size一样
        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;
    }
}
2.2 增加元素

(1)add(E e)及其扩容

public boolean add(E e) {
	// 进行扩容
	ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
// 扩容时进行调用
private void ensureCapacityInternal(int minCapacity) {
   ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
	// 当用无参构造器实例化且第一次调用时,elementData总是会被初始化为10
	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		// 底层用到了三目运算
		return Math.max(DEFAULT_CAPACITY, minCapacity);
     }
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
	modCount++;
	// 判断需要的最小容量是否超过当前elementData的容量
    if (minCapacity - elementData.length > 0)
    	grow(minCapacity);
}

 private void grow(int minCapacity) {
 	// overflow-conscious code
    int oldCapacity = elementData.length;
    // 扩容的核心算法
    // >> :表示右移,右移几就是除以2的几次幂
    // << :表示左移,左移几就是乘以2的几次幂
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 扩容1.5倍后仍无法满足最小容量
    if (newCapacity - minCapacity < 0)
    	// 那么就用该minCapacity
    	newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 数组的扩容和拷贝
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // overflow
    	throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
    }

(2)add( int index, E element )

	// 在指定索引处插入元素
public void add(int index, E element) {
 	// 检查索引是否合法
	rangeCheckForAdd(index);
	// 进行扩容
    ensureCapacityInternal(size + 1);
    // 将目前index处和它后面的元素整体后移一个位置  
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    // 更新该索引处的值
    elementData[index] = element;
    size++;
}

// 检查索引是否合法的方法
private void rangeCheckForAdd(int index) {
	// 判断索引是否大于容量或小于0
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

(3) addAll(Collection<? extends E> c)

public boolean addAll(Collection<? extends E> c) {
	// 将有数据的集合转化为数组
	Object[] a = c.toArray();
	// 有数据集合长度赋值给numNew
    int numNew = a.length;
    // 校验以及扩容
    ensureCapacityInternal(size + numNew);  
    //进行拷贝
    System.arraycopy(a, 0, elementData, size, numNew);
    //集合长度进行更改
    size += numNew;
    //根据numNew返回是否添加成功
    return numNew != 0;
}

结论:底层使用了System.arraycopy方法进行了拷贝

(4)addAll(int index, Collection<? extends E> c)

public boolean addAll(int index, Collection<? extends E> c) {
	// 检查索引是否合法
	rangeCheckForAdd(index);
    Object[] a = c.toArray();
    int numNew = a.length;
    // 进行扩容
    ensureCapacityInternal(size + numNew);  
    // numMoved :代表要移动元素的个数
    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;
}
	       
2.3 修改元素值
public E set(int index, E element) {
	// 检查索引是否合法
	rangeCheck(index);
	// 取出旧值
    E oldValue = elementData(index);
    // 替换旧值
    elementData[index] = element;
    // 返回旧值
    return oldValue;
 }

2.4 获取元素值
public E get(int index) {
	// 检查索引是否合法
	rangeCheck(index);
	// 返回要获取的值
	return elementData(index);
}
// 获取指定的元素
E elementData(int index) {
    return (E) elementData[index];
}
2.5 转化的方法

toString是属于AbstractCollection抽象类里面的方法

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(' ');
     }
}
2.6 删除元素

(1)删除单个元素1

// 删除指定索引处的元素
    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);
        // 将最后一个元素置为null
        elementData[--size] = null; 
	    // 返回被删除元素的值
        return oldValue; 
    }

(2)删除单个元素2

 // 删除指定元素 
    public boolean remove(Object o) {
    // ArrayList中允许添加为null的元素
        if (o == null) { 
            // 遍历并删除第一个为null的元素
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                 // 该方法与remove(int index)类似,仅仅少了边界检查且没有返回值
                    fastRemove(index);
                    return true;
                }
        } else {
            // 遍历并删除第一个相等的元素
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        // 未找到与o匹配的元素,返回false
        return false;
    }

(3)clear 清空元素

public void clear() {
	// 实际修改次数自增
	modCount++;
    // 遍历集合
    for (int i = 0; i < size; i++)
    	// 把数组中的每一个位置为null,让垃圾回收期尽早回收
       elementData[i] = null;
	// 把集合长度设为0
    size = 0;
}

3、面试相关

说说1.7和1.8版本初始化的时候的区别么?

1.7以前会调用this(10)才是真正的容量为10
1.8是默认走了空数组,只有第一次add的时候容量会变成10。

ArrayList是线程安全的么?

不是,Vector是线程安全版本的数组容器。

Vector的实现很简单,就是把所有的方法统统加上synchronized就

ArrayList的遍历和LinkedList遍历性能比较如何?

ArrayList比LinkedList快,ArrayList遍历最大的优势在于内存的连续性,CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值