ArrayList源码分析

事先说明,这是原创文章,不是从网上扒的,从晚上6点半写到了8点才搞定,饭都还没吃

如果你是新手,请一步一步的看,你一定能看懂的

说明:为了去掉原来的英文注释,我把ArrayList改为了ArrayListsrc

import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.RandomAccess;

/**
 * 
 * @author yj
 * @date 2015-12-5 下午6:27:51
 * @description: ArrayList的源码分析
 * @param <E>
 * 
 * @Declaration 关于容量(Capacity)和大小(Size)的理解
 * 	容量:ArrayList可以容纳的元素有多少个,比如初始化大小为10
 * 	大小:是说当前ArrayList中有多少个元素.想想平时怎么用size()的就很容易明白了
 */
//实现的接口介绍:RandomAccess(用于随机访问),Cloneable(用于克隆),Serializable(用于对象的序列化)
public class ArrayListSrc<E> extends AbstractList<E> implements List<E>,
		RandomAccess, Cloneable, java.io.Serializable {
	private static final long serialVersionUID = 8683452581122892189L;

	// 底层使用一个Object数组实现的
	private transient Object[] elementData;

	// elementData中有多少个元素
	private int size;

	// 以指定的容量大小来初始化数组
	public ArrayListSrc(int initialCapacity) {
		super();
		// 如果初始化容量<0,抛出非法参数异常
		if (initialCapacity < 0)
			throw new IllegalArgumentException("Illegal Capacity: "
					+ initialCapacity);
		//初始化数组
		this.elementData = new Object[initialCapacity];
	}
	
	/*
	 * 这是默认的构造函数,调用了ArrayListSrc(int initialCapacity)
	 * 也就是说默认的数组容量是10
	 */
	public ArrayListSrc() {
		this(10);
	}
	
	/*
	 * 将一个集合转换为数组并赋给elementData
	 * <? extends E>是泛型的范围限定,E是ArrayList类的泛型,表示E及E的子类
	 */
	public ArrayListSrc(Collection<? extends E> c) {
		elementData = c.toArray();
		size = elementData.length;
		//c.toArray()方法可能不能正确的转换,这里加了判断
		if (elementData.getClass() != Object[].class)
			//copyOf:将elementData[0-size]复制到Object[]中
			elementData = Arrays.copyOf(elementData, size, Object[].class);
	}
	
	/*
	 * 先看看老外起的方法名:trimToSize,削减到大小(size)
	 * 也就是说将当前的容量(Capacity)减小到size
	 * 比如现在ArrayList的容量是10,但是里面只有5个元素,就把容量减小到5
	 */
	public void trimToSize() {
		//modCount是父类AbstractList中的一个变量,用于记录被修改了多少次
		modCount++;
		int oldCapacity = elementData.length;
		if (size < oldCapacity) {
			//这里就是实现削减的方式:已size为数组长度赋值一份原来的elementData
			elementData = Arrays.copyOf(elementData, size);
		}
	}

	/*
	 * 简单的说就是在必要的时候给数组扩容,记住在我们new一个数组是,长度就已经确定了,
	 * 扩容只能再创建一个长度更大数组,并把原来的复制过去
	 */
	public void ensureCapacity(int minCapacity) {
		//修改次数+1
		modCount++;
		//获取底层数组的长度
		int oldCapacity = elementData.length;
		//如果参数>数组长度,说明需要扩容了
		if (minCapacity > oldCapacity) {
			//保存原来的数组
			Object oldData[] = elementData;
			//新的数组长度是原来的1.5倍
			int newCapacity = (oldCapacity * 3) / 2 + 1;
			//如果扩容以后还是不够,那么直接把minCapacity作为新数组长度
			if (newCapacity < minCapacity){
				newCapacity = minCapacity;
			}
			//copyOf内部创建一个新数组,并把elementData复制进去,最后的返回值赋给elementData
			//要想看的更清楚可以选中copyOf按F3进去看源码
			elementData = Arrays.copyOf(elementData, newCapacity);
		}
	}

	//获取ArrayList当前的元素个数
	public int size() {
		return size;
	}

	//判断是否为空
	public boolean isEmpty() {
		return size == 0;
	}

	//判断是否包含一个元素
	public boolean contains(Object o) {
		//indexOf:如果能索引到该元素,返回值是>=0的,如果索引不到返回-1
		return indexOf(o) >= 0;
	}

	//获取元素的索引
	public int indexOf(Object o) {
		//判断o是否是一个空引用,如果调用该方法是为:indexOf(null)
		if (o == null) {
			//源码的for和if都没有{},看得我好难受,果断加上
			for (int i = 0; i < size; i++){
				//如果某个元素为null,返回索引
				if (elementData[i] == null){
					return i;
				}
			}
				
		} else {
			//暴力搜索,一个一个比较,相等就返回索引
			for (int i = 0; i < size; i++)
				if (o.equals(elementData[i]))
					return i;
		}
		//找不到就返回-1
		return -1;
	}
	
	//搜索元素o最后出现的位置
	public int lastIndexOf(Object o) {
		//o==null同indexOf
		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;
		}
		//找不到就返回-1
		return -1;
	}

	/*
	 * 覆写Object类的clone方法,可以看出是一个深克隆
	 * 友情复习:如果一个类中的成员遍历都是基本数据类型,那么就是浅克隆
	 * 而ArrayList中有Object[] elementData,这是引用类型遍历,所以是深克隆
	 */
	public Object clone() {
		try {
			ArrayListSrc<E> v = (ArrayListSrc<E>) super.clone();//这一步只是浅克隆
			v.elementData = Arrays.copyOf(elementData, size);//elementData也要克隆才是深克隆
			v.modCount = 0;//克隆后初始话记录修改次数的变量modCount为0
			return v;
		} catch (CloneNotSupportedException e) {
			// this shouldn't happen, since we are Cloneable
			throw new InternalError();
		}
	}

	//把ArrayList转化为数组
	public Object[] toArray() {
		//直接拷贝了一个elementData[size]数组
		return Arrays.copyOf(elementData, size);
	}

	//参数传入一个数组,输出和参数同类型的数组(没用过)
	public <T> T[] toArray(T[] a) {
		if (a.length < size){
			//把elementData以size大小赋值到T[]数组中并返回
			return (T[]) Arrays.copyOf(elementData, size, a.getClass());
		}
		//所以说一般传入一个长度和size大小相同的数组
		System.arraycopy(elementData, 0, a, 0, size);
		//如果传入的数组长度>size就设置a[size]为空
		if (a.length > size)
			a[size] = null;
		return a;
	}

	//获取index角标处的元素
	public E get(int index) {
		//检查角标是否越界
		RangeCheck(index);
		return (E) elementData[index];
	}

	//用指定的元素替代指定位置上的元素
	public E set(int index, E element) {
		//检查角标是否越界
		RangeCheck(index);

		//获取原来的元素
		E oldValue = (E) elementData[index];
		//设置新元素
		elementData[index] = element;
		//以前位于该指定位置上的元素(虽然我也很好奇有返回值)
		return oldValue;
	}

	//添加元素
	public boolean add(E e) {
		//用来保证当前的容量大小可以保存该元素,也就是添加这个元素超过了容量就要扩容了
		ensureCapacity(size + 1);
		//添加到最后的位置
		elementData[size++] = e;
		//添加成功就返回true
		return true;
	}

	//在指定的位置添加元素
	public void add(int index, E element) {
		//如果index大于size或者<0就抛出异常
		if (index > size || index < 0)
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
					+ size);

		ensureCapacity(size + 1); // Increments modCount!!增加了修改次数
		/*
		 * 说下这个方法吧,arraycopy:把elementData<1>数组的index位置  复制到   elementData<2>的index+1位置
		 * 复制size-index个元素,相当于index后面的元素整体后移一位,想想让自己如何完成这个功能,就很好想通了
		 */
		System.arraycopy(elementData, index, elementData, index + 1, size
				- index);
		//index后面的元素都后移一位了,那么把element放在这就行了
		elementData[index] = element;
		//最后size++
		size++;
	}

	//删除指定位置的元素
	public E remove(int index) {
		//这俩不说了
		RangeCheck(index);
		modCount++;
		
		//获取index处的元素
		E oldValue = (E) elementData[index];

		//计算要移动的数目,也就是说把index处的元素删除了,后面都得往前移一位
		int numMoved = size - index - 1;
		if (numMoved > 0){
			//后面的元素都往前移一位
			System.arraycopy(elementData, index + 1, elementData, index,
					numMoved);
		}
		/*
		 * 这里很重要,设置最后一个元素为null,可以让gc来回收,避免了内存泄露
		 * 友情复习(我是好人):内存泄露就是说一个引用不指向任何一个对象,但是它还占据着内存空间
		 * 如果把它设置为null,gc就可以回收了
		 */
		elementData[--size] = null; // Let gc do its work

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

	//删除指定的元素(如果一步一步看的这里不用说了,就说说fastRemove吧)
	public boolean remove(Object o) {
		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;
	}

	//首先这是一个private方法,属于类内部,用于删除index角标处的元素
	private void fastRemove(int index) {
		modCount++;
		//和remove(int index)一样
		int numMoved = size - index - 1;
		if (numMoved > 0)
			System.arraycopy(elementData, index + 1, elementData, index,
					numMoved);
		elementData[--size] = null; // Let gc do its work
	}

	//清空集合
	public void clear() {
		modCount++;
		//把所有元素都设置为空引用
		for (int i = 0; i < size; i++)
			// Let gc do its work 设置为null,交给gc回收
			elementData[i] = null;
		//size设置为0
		size = 0;
	}

	//把一个已有的集合添加到ArrayList中
	public boolean addAll(Collection<? extends E> c) {
		//先转成数组
		Object[] a = c.toArray();
		//获取数组长度
		int numNew = a.length;
		//保证ArrayList的容量
		ensureCapacity(size + numNew); // Increments modCount
		//把转换后的数组赋值到elementData数组中,长度为numNew
		System.arraycopy(a, 0, elementData, size, numNew);
		//size也会增加
		size += numNew;
		//新手看不懂返回值的话就不看return,只看numNew != 0,这个逻辑表达式要么为true,要么为false
		return numNew != 0;
	}

	//这个方法是对上面方法的一个小增强:在指定的位置添加一个集合
	public boolean addAll(int index, Collection<? extends E> c) {
		//判断索引
		if (index > size || index < 0)
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
					+ size);

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

		int numMoved = size - index;
		if (numMoved > 0){
			//不用之处只是复制到elementData数组的位置为index + numNew
			System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
		}
		//这是添加一个空集合的情况
		System.arraycopy(a, 0, elementData, index, numNew);
		size += numNew;
		return numNew != 0;
	}

	//删除角标在[fromIndex,toIndex)范围内的元素,我用区间表示范围,有高中知识的应该能看懂吧?
	protected void removeRange(int fromIndex, int toIndex) {
		modCount++;
		int numMoved = size - toIndex;

		System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);

		//这是计算要从哪里开始删
		int newSize = size - (toIndex - fromIndex);
		while (size != newSize){
			// Let gc do its work
			elementData[--size] = null;
		}
	}

	//很简单的一个角标判断
	private void RangeCheck(int index) {
		if (index >= size)
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
					+ size);
	}

	/*
	 * writeObject和readObject都是用于序列化的方法,我主要将ArrayList的线性表部分,只对序列化做一个简单介绍给新手
	 * 序列化:
	 * 	我们代码中的对象都会在程序运行结束后销毁,那么能不能存到硬盘上,
	 * 	或者直接通过网络传输呢?答案是可以,只要实现了序列化接口(Serializable)即可
	 * 这是一个标志接口(Cloneable也是),意思是这个接口中什么都没有,相当于给类打个标签,实现该接口的就可以序列化
	 * writeObject:将所有元素写入到一个输出流中
	 * readObject:从一个输入流中读取元素
	 * 
	 * @question 留个疑问给新手,为什么ArrayList已经实现了序列化接口还有写这两个方法
	 * 提示:transient Object[] elementData,transient是干嘛的?
	 */
	private void writeObject(java.io.ObjectOutputStream s)
			throws java.io.IOException {
		// Write out element count, and any hidden stuff
		int expectedModCount = modCount;
		s.defaultWriteObject();

		// Write out array length
		s.writeInt(elementData.length);

		// Write out all elements in the proper order.
		for (int i = 0; i < size; i++)
			s.writeObject(elementData[i]);

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

	}

	private void readObject(java.io.ObjectInputStream s)
			throws java.io.IOException, ClassNotFoundException {
		// Read in size, and any hidden stuff
		s.defaultReadObject();

		// Read in array length and allocate array
		int arrayLength = s.readInt();
		Object[] a = elementData = new Object[arrayLength];

		// Read in all elements in the proper order.
		for (int i = 0; i < size; i++)
			a[i] = s.readObject();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值