ArrayList源码

ArrayList:

  • 基于数组实现可自动扩容的集合列表
  • 允许插入NULL元素。
  • 非线程安全
  • 基于位置查询速度快, O(1)
  • 指定位置新增和删除慢,涉及元素拷贝移动

1、接口

1.1、Iterable


提供foreach循环支持,通过iterator方法可以得到集合的迭代器对象Iterator。

1.2、Collection


集合的顶层接口,抽象了集合的一些通用操作方法,实现此接口的类都是抽象类或其他接口

1.3、AbstractCollection


给出了Collection接口的骨架实现,Collection抽象了集合的通用操作,AbstractCollection则封装了操作的通用逻辑。
这里要注意三个方法

public boolean add(E e) {
        // 直接抛异常,意味着需要子类去实现add方法,延迟到子类实现
	throw new UnsupportedOperationException();
}
// 抽象方法,延迟到子类实现
public abstract Iterator<E> iterator();
// 抽象方法,延迟到子类实现
public abstract int size();

AbstractCollection的大部分逻辑都是依赖iterator完成的

1.4、List


定义了有序集合(插入顺序)的通用方法,允许插入null,在Collection的基础上又抽象了get(int)、add(int,E)等方法。进一步完善了集合的操作功能。
Collection提供了集合的最小化操作,List在Collection的基础上进行了扩展,对功能进行了增强。

1.5、AbstractList


给出了List接口的骨架实现,并继承了AbstractCollection的功能。
这个类主要服务于已数组为底层的支持RandomAccess访问的集合。

2、方法

2.1、add(E e)

向集合中添加元素,需要注意的是,由于列表无参构造器初始化默认返回空数组,在进行扩容计算时加了特殊判断

// 数组允许的最大长度,文档给的解释是有些虚拟机会在数组中保留一些头信息,即数组前几位可能存储的不是真正的数据
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 在数组元素末尾新增元素
public boolean add(E e) {
      // 计算添加元素数组所需最小值,如果数组长度小于最小值则以1.5倍容量扩容,如果这里size=Integer.MAX_VALUE,则size + 1会溢出成为一个小于0的数
      ensureCapacityInternal(size + 1);
      elementData[size++] = e;
      return true;
}
// 扩容处理
private void ensureCapacityInternal(int minCapacity) {
      ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 获取新增元素所需的最小数组长度值
private static int calculateCapacity(Object[] elementData, int minCapacity) {
      // 这里做了初始化后的特殊判断,如果数组为空,则取默认长度与最小长度两者的最大值
      if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
      }
      return 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;
        // 以1.5倍扩容得到扩容长度,这个扩容长度值是存在溢出风险的,因为int的最大值是2147483647
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 这里主要是针对使用默认构造器初始化时,延迟初始化数组,直到第一个元素写入时创建数组。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 溢出判断
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
}
// 扩容溢出处理
private static int hugeCapacity(int minCapacity) {
        // 溢出处理,当前数组长度已经是Integer.MAX_VALUE了,无法继续添加元素,抛出异常
	if (minCapacity < 0) // overflow
		throw new OutOfMemoryError();
        // 虽然有数组最大值的兼容处理,但是还是可以继续扩容至Integer.MAX_VALUE
	return (minCapacity > MAX_ARRAY_SIZE) ?
		Integer.MAX_VALUE :
		MAX_ARRAY_SIZE;
}

2.1.1、逻辑

扩容逻辑是添加元素的难点和重点,因为这里做了溢出的逻辑处理,贴个图好理解一些,1431655765是一个临界值,扩容后会得到Integer.MAX_VALUE
假设当前数组 length = size = 1431655765,继续添加元素

  1. 取得新增元素数组下标,minCapacity = size + 1 = 1431655766
  2. 进入ensureExplicitCapacity方法 --> minCapacity - elementData.length = 1 > 0 --> 进入grow(minCapacity)方法
  3. oldCapacity = 1431655765,newCapacity = 2147483647 = Integer.MAX_VALUE,第一个if不满足[if (newCapacity - minCapacity < 0)],newCapacity - MAX_ARRAY_SIZE = 8,满足第二个if,进入hugeCapacity(minCapacity)方法
  4. minCapacity > 0,minCapacity > MAX_ARRAY_SIZE 为 true, 返回 Integer.MAX_VALUE。
  5. 执行elementData = Arrays.copyOf(elementData, newCapacity),将原数组扩容复制到新数组。新数组容量为Integer.MAX_VALUE
  6. 在1431655766设置元素值
  7. 返回true,新增结束
    接着继续新增一个元素
  8. 取得新增元素数组下标,minCapacity = size + 1 = -2147483648,
  9. 进入ensureExplicitCapacity方法 --> minCapacity - elementData.length = 1 > 0 --> 进入grow(minCapacity)方法
  10. oldCapacity = 2147483647,newCapacity = -1073741826,newCapacity - minCapacity = 1073741822 > 0, 第一个if不满足,newCapacity - MAX_ARRAY_SIZE = 1073741831 > 0,满足第二个if,进入hugeCapacity(minCapacity)方法
  11. minCapacity = -2147483648 < 0,抛出异常

2.2、E remove(int index)

删除集合中指定位置的元素, 位置右边的元素会左移一位, 并置空末位元素

public E remove(int index) {
        // 边界校验
	rangeCheck(index);

	modCount++;
        // 获取指定位置元素
	E oldValue = elementData(index);

	int numMoved = size - index - 1;
        // 移动元素, elementData中index+1位置开始往后numMoved个元素复制到elementData中index开始往后numMoved个位置的元素, 即删除元素位置后边的元素左移一位
	if (numMoved > 0)
		System.arraycopy(elementData, index+1, elementData, index,
						 numMoved);
	elementData[--size] = null; // clear to let GC do its work

	return oldValue;
}

2.3、E set(int index, E element)

修改指定位置元素

public E set(int index, E element) {
	rangeCheck(index);

	E oldValue = elementData(index);
	elementData[index] = element;
	return oldValue;
}

2.4、boolean contains(Object o)

查询元素信息

public boolean contains(Object o) {
	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;
}

循环数组,比较查询值,返回下标

3、迭代器

3.1、Itr

private class Itr implements Iterator<E> {
        // 即将返回的元素下标,next返回的下标数据
	int cursor;       // index of next element to return
        // 上一个元素下标,remove删除此下标数据, 虽然是上一个元素的下标,但是如果调用remove,lastRet会被重置,所以并不可靠
	int lastRet = -1; // index of last element returned; -1 if no such
	int expectedModCount = modCount;

	Itr() {}

	public boolean hasNext() {
		return cursor != size;
	}

	@SuppressWarnings("unchecked")
	public E next() {
		checkForComodification();
		int i = cursor;
		if (i >= size)
			throw new NoSuchElementException();
		Object[] elementData = ArrayList.this.elementData;
		if (i >= elementData.length)
			throw new ConcurrentModificationException();
                // 右移即将返回的元素下标
		cursor = i + 1;
                // 右移上一个元素下标,并返回移动后的下标元素
		return (E) elementData[lastRet = i];
	}

	public void remove() {
		if (lastRet < 0)
			throw new IllegalStateException();
		checkForComodification();

		try {
                        // 清空上一个元素的下标数据,并将后边的数据左移一位
			ArrayList.this.remove(lastRet);
                        // 将上一个元素的下标赋值为将要返回的下标,例如,删除下标为2的元素,2后边的元素会左移一位,下次next将要返回的下标元素还是2
			cursor = lastRet;
                        // 重置上一个元素的下标,防止连续调用remove
			lastRet = -1;
			expectedModCount = modCount;
		} catch (IndexOutOfBoundsException ex) {
			throw new ConcurrentModificationException();
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public void forEachRemaining(Consumer<? super E> consumer) {
		Objects.requireNonNull(consumer);
		final int size = ArrayList.this.size;
		int i = cursor;
		if (i >= size) {
			return;
		}
		final Object[] elementData = ArrayList.this.elementData;
		if (i >= elementData.length) {
			throw new ConcurrentModificationException();
		}
		while (i != size && modCount == expectedModCount) {
			consumer.accept((E) elementData[i++]);
		}
		// update once at end of iteration to reduce heap write traffic
		cursor = i;
		lastRet = i - 1;
		checkForComodification();
	}

	final void checkForComodification() {
		if (modCount != expectedModCount)
			throw new ConcurrentModificationException();
	}
}
public Iterator<E> iterator() {
	return new Itr();
}

从源码看出,iterator仅支持顺序迭代集合元素,支持迭代过程中删除元素,不支持新增和修改。

3.2、ListIterator


ListIterator接口是一个集合列表迭代器,允许前序和后序遍历集合,迭代期间修改列表,并获得迭代器在列表中的当前位置。

3.3、ListItr

private class ListItr extends Itr implements ListIterator<E> {
	ListItr(int index) {
		super();
		cursor = index;
	}

	public boolean hasPrevious() {
		return cursor != 0;
	}

	public int nextIndex() {
		return cursor;
	}

	public int previousIndex() {
		return cursor - 1;
	}

	@SuppressWarnings("unchecked")
	public E previous() {
		checkForComodification();
		int i = cursor - 1;
		if (i < 0)
			throw new NoSuchElementException();
		Object[] elementData = ArrayList.this.elementData;
		if (i >= elementData.length)
			throw new ConcurrentModificationException();
                // 左移即将返回的下标值
		cursor = i;
                // 即将返回的下标赋值给上一个元素下标,即两个下标指向同一位置,并返回元素
		return (E) elementData[lastRet = i];
	}

	public void set(E e) {
		if (lastRet < 0)
			throw new IllegalStateException();
		checkForComodification();

		try {
                        // 修改上一个元素下标对应的数据
			ArrayList.this.set(lastRet, e);
		} catch (IndexOutOfBoundsException ex) {
			throw new ConcurrentModificationException();
		}
	}

	public void add(E e) {
		checkForComodification();

		try {
			int i = cursor;
                        // 右移下标后的所有元素,并在即将返回的元素之前插入一个元素信息
			ArrayList.this.add(i, e);
                        // 右移即将返回的元素下标
			cursor = i + 1;
                        // 重置上一个元素下标
			lastRet = -1;
			expectedModCount = modCount;
		} catch (IndexOutOfBoundsException ex) {
			throw new ConcurrentModificationException();
		}
	}
}
/**      从指定位置开始获取迭代器      */
public ListIterator<E> listIterator(int index) {
	if (index < 0 || index > size)
		throw new IndexOutOfBoundsException("Index: "+index);
	return new ListItr(index);
}
// 从头开始获取迭代器
public ListIterator<E> listIterator() {
	return new ListItr(0);
}

ListItr继承了Itr类并实现了ListIterator,意味着ListItr可以对集合进行前序和后序遍历,并且在Itr的基础上可以在迭代过程中对集合进行修改操作。

方法 描述
next() Ltr实现,lastRet表示当前返回数据的下标,cursor为即将返回数据的下标。
previous() 用临时变量记录cursor-1,然后lastRet和cursor指向了同一个下标值,返回临时变量的下标元素。
set(E) 修改lastRet位置的元素信息
add(E) cursor后面所有的元素右移一位,然后在cursor位置插入元素,然后重置lastRet为-1,由于cursor右移,所以插入的数据在调用next方法会跳过插入值
remove(),set(E),add(E)方法由于会重置lastRet,所以三个是互斥的,不能连续出现。
posted @ 2020-11-13 07:12  Simple°  阅读(216)  评论(0编辑  收藏  举报