ArrayList源码解析

版本:jdk1.8

ArrayList 数组列表

底层由数组实现
线程不安全
适合随机查找、更新(通过下标 时间复杂度为O(1))
不适合大量的新增、删除(涉及数组的移动)

底层数据结构:数组
在这里插入图片描述

ArrayList相关的UML

在这里插入图片描述

进入源码

看看有哪些属性

	/**
	 * 默认容量 10
	 */
	private static final int DEFAULT_CAPACITY = 10;

	/**
	 * 长度为0数组
	 */
	private static final Object[] EMPTY_ELEMENTDATA = {};

	/**
	 * 默认长度为0数组
	 */
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

	/**
	 * 对象数组 存放数据的结构  序列化时会忽略
	 */
	transient Object[] elementData;

	/**
	 * 集合大小
	 */
	private int size;
	/**
	 * 数组的最大大小
	 */
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

再看具体的源码之前,先来看看数组的Arrays.copyOf底层实现
在这里插入图片描述
最终是由System.arraycopy(src, srcPos, dest, destPos, length)来实现从原数组的copy数据到扩容后的新数组

src 源数组 srcPos 源数组要复制的起始下标位置 dest 目标数组 destPos 目标数组起始下标位置 length 要复制的长度

理解这里很重要,因为很多地方都会涉及到数组的拷贝
先来看看几个构造

	/**
	 * 根据initialCapacity初始化elementData
	 * @param initialCapacity 初始化数组的大小
	 */
	public ArrayList(int initialCapacity) {
		if (initialCapacity > 0) {
			// 初始化initialCapacity大小的数组
			this.elementData = new Object[initialCapacity];
		} else if (initialCapacity == 0) {
			// 初始化个空数组
			this.elementData = EMPTY_ELEMENTDATA;
		} else {
			throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
		}
	}
	/**
	 * elementData默认长度为0数组
	 */
	public ArrayList() {
		this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	}
	/**
	 * 直接把elementData指向集合转成的数组
	 * @param c 集合
	 */
	public ArrayList(Collection<? extends E> c) {
		elementData = c.toArray();//把elementData指向集合转成的数组
		if ((size = elementData.length) != 0) {//如果用户传入的集合不为空
			if (elementData.getClass() != Object[].class)//数组的类型不是对象数组就要copy一份原数组到对象数组类型
				elementData = Arrays.copyOf(elementData, size, Object[].class);
		} else {
			this.elementData = EMPTY_ELEMENTDATA;//空数组
		}
	}

ArrayList.add
可以在末尾、指定位置插入数据,里面会涉及数组的扩容,新数组的大小为原数组的1.5倍并将原数组的数据copy到新数组中再把要添加的数据添加到新的数组中,整个扩容的流程就在这下面了,后面有涉及的话,代码也就不贴出来了

	/**
	 *	 默认是末尾添加 
	 */
	public boolean add(E e) {
		//数组扩容的处理 需要扩容的话 新数组的大小为原数组的1.5 并将原数组的数据copy到新数组中 elementData指向新数组
		ensureCapacityInternal(size + 1);
		elementData[size++] = e;// 赋值 数组大小加一
		return true;
	}
	/*
	 * 在index位置添加数据
	 */
	public void add(int index, E element) {
		rangeCheckForAdd(index);//检测是否越界 下标不能大于等于当前集合大小且下标不能小于0

		ensureCapacityInternal(size + 1); // 数组扩容
		//数组自身移动  从index下标开始的(size - index)个数  每个数据的下标向后移动一位  下标index正好空出来
		System.arraycopy(elementData, index, elementData, index + 1, size - index);
		elementData[index] = element;//赋值
		size++;//数组大小加一
	}
	/**
	 *  数组扩容
	 */
	private void ensureCapacityInternal(int minCapacity) {//minCapacity 所需的最小容量大小
		ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
	}

	/**
	 *	当所需的最小容量大小大于当前数组的大小 数组扩容
	 */
	private void ensureExplicitCapacity(int minCapacity) {
		modCount++;//修改次数加一
		// overflow-conscious code
		if (minCapacity - elementData.length > 0)// 所需的最小容量大小大于当前数组的大小
			grow(minCapacity);// 数组扩容
	}
	/**
	 * 扩容
	 */
	private void grow(int minCapacity) {
		// overflow-conscious code
		int oldCapacity = elementData.length;// 当前容量
		// 相当于 newCapacity = 1.5 * oldCapacity
		int newCapacity = oldCapacity + (oldCapacity >> 1);
		if (newCapacity - minCapacity < 0)
			newCapacity = minCapacity;
		if (newCapacity - MAX_ARRAY_SIZE > 0)
			newCapacity = hugeCapacity(minCapacity);
		// 将当前数组中的数据copy到长度为newCapacity的新数组中 elementData指向新的数组
		elementData = Arrays.copyOf(elementData, newCapacity);
	}

ArrayList.get
通过index获取数组中的数据,所以ArrayList随机访问很快

	/**
	 * 获取下标为index的数据
	 */
	public E get(int index) {
		rangeCheck(index);// 下标范围的校验

		return elementData(index);// 获取下标为index的数据
	}

	/**
	 * 获取下标为index的数据
	 */
	@SuppressWarnings("unchecked")
	E elementData(int index) {
		return (E) elementData[index];
	}

ArrayList.set
在指定位置覆盖原来的值

	/**
	 * 用element覆盖掉下标为index的数据 并返回旧值
	 */
	public E set(int index, E element) {
		rangeCheck(index);// 数组越界校验

		E oldValue = elementData(index);// 获取到旧值
		elementData[index] = element;// 重新赋值
		return oldValue;// 返回旧值
	}

ArrayList.remove
删除集合中的数据

	/**
	 * 删除对象o
	 * o为null 用==比较
	 * 否者用equals 
	 * 找到之后相等对象的下标   快速删除 
	 */
	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);//快速删除 下标为index的数据
					return true;
				}
		}
		return false;
	}
	/*
	 * 快速删除 下标为index的数据
	 */
	private void fastRemove(int index) {
		modCount++;
		int numMoved = size - index - 1;
		if (numMoved > 0)//数组自身移动  从(index+1)下标开始的numMoved个数  每个数据的下标向前移动一位  下标(size-1)正好空出来
			System.arraycopy(elementData, index + 1, elementData, index, numMoved);
		elementData[--size] = null; // 赋null 之前指向的数据会被垃圾回收器处理
	}

**需要注意的是:**在使用ArrayList.subList的时候要特别注意,获取到的List,里面会有原ArrayList的引用

	/**
	 * 生成一个SubList对象 当前的Arraylist会被作为一个属性
	 * 阿里java开发手册说道
	 * 2.   【强制】ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常:
	 * java.util.RandomAccessSubList cannot be cast to java.util.ArrayList; 
	 * 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,
	 * 对于SubList子列表的所有操作最终会反映到原列表上。
	 * 3.   【强制】在subList场景中,高度注意对原集合元素个数的修改,
	 * 	会导致子列表的遍历、增加、删除均产生ConcurrentModificationException 异常。
	 */
	public List<E> subList(int fromIndex, int toIndex) {
		subListRangeCheck(fromIndex, toIndex, size);
		return new SubList(this, 0, fromIndex, toIndex);
	}

这里只是贴出了一些比较重要的方法,想要查看更完整的ArrayList源码解析点击这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值