源码阅读系列——基础篇(ArrayList 集合源码分析)
2020-06-29 05:02
80 查看
ArrayList是我们非常常用的一个线程不安全的List集合。ArrayList和Vector大体比较相似。我们继续阅读源码找答案。
1、类定义
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final int DEFAULT_CAPACITY = 10; // 默认容量 private static final Object[] EMPTY_ELEMENTDATA = {}; // 无元素的默认数组,见构造函数1 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 无元素的默认数组定义,见构造函数2 transient Object[] elementData; // 元素数组 private int size; // 元素数量 // 构造函数1:指定大小的构造函数 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); } } // 构造函数2 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } }
从上面的源码,得出以下:
ArrayList默认是一个空数组。只有制定长度时,才会生产一个定长数组。
2、扩容机制
已增加元素为例
public void add(int index, E element) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); // 整体后移index后的所有元素 elementData[index] = element; size++; }
我看一下ensureCapacityInternal实现
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //如果当前长度不到DEFAULT_CAPACITY(10)的长度,先设置为DEFAULT_CAPACITY } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);// 原来长度,加上原长度右移一位(相当于1.5倍原长度) if (newCapacity - minCapacity < 0) 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) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
由源码得出:
1、如果长度不到10,则先扩容到10。
2、如果长度大于10了,则每次扩容1.5倍。比Vector每次扩容2倍,省空间,如果不频繁扩容,效率差不多。
3、迭代器
我们常常有这样的需求,遍历数组的过程中,修改元素,但是如果像下面这样的写法,一定会有数组越界的问题:
public void removeElement(int value){ for (int i = 0 ; i < elementList.size() ; i++){ if(value == elementList.get(i).value){ elementList.remove(i); } } }
Java提供了迭代器的方式来满足以上需求,看一下迭代器的主要代码:
private class Itr implements Iterator<E> { protected int limit = ArrayList.this.size; int cursor; // 游标,可以理解为遍历数组的指针(或者索引)。 int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; // modCount表示List被修改操作的次数。(我们在List里面的所有影响list数组的操作,都会自增1,比如List.remove(),List.clear()) public boolean hasNext() { return cursor < limit; // 是否已经遍历到头 } @SuppressWarnings("unchecked") public E next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); // 如果发现迭代器在遍历的过程中,modCount发生变化了,则抛异常,这就是Fast-fail机制 int i = cursor; if (i >= limit) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); // 发现数组长度越界,抛异常(理论上遍历的过程不会越界,所以理解迭代器之外有改动) cursor = i + 1; // 索引后移一位,表示下一个要被遍历的元素 return (E) elementData[lastRet = i]; // lastRet赋值为我们当前遍历的元素 } public void remove() { if (lastRet< 0) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); try { ArrayList.this.remove(lastRet); // 移除当前遍历到的元素 cursor = lastRet;// 下一个遍历的元素,位置index也需要-1 lastRet = -1; expectedModCount = modCount; // 更新modCount limit--; // 长度减1 } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
从上面的源码解析,我们大致可以发现迭代器的作用:他其实可以理解建立了一个封闭的空间,可以让开发者在遍历的同时修改元素,甚至影响到元素数组的大小。而由于迭代器里面实现一套对长度、遍历索引的动态变更,所以在遍历的过程中,我们不能在迭代器之外对数组有任何改动。
相关文章推荐
- 源码阅读系列——基础篇(CopyOnWriteArrayList 集合源码分析)
- 源码阅读系列——基础篇(ConcurrentHashMap 集合源码分析)
- 源码阅读系列——基础篇(HashMap 集合源码分析)
- 源码阅读系列——基础篇(LinkedList 集合源码分析)
- Java集合系列之ArrayList源码分析
- Java集合源码分析系列-(一)ArrayList源码剖析
- Java集合系列:-----------03ArrayList源码分析
- Java集合系列之ArrayList源码分析
- 集合系列—ArrayList源码分析
- Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java集合干货——ArrayList源码分析
- Java集合源码分析之ArrayList
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java集合-ArrayList深入浅出源码分析
- Java集合系列之HashMap源码分析
- 集合系列—HashMap源码分析
- 深入 Java 集合系列之 ArrayList 源码解读
- java源码阅读系列-ArrayList