我们知道list集合的底层是数组,可是究竟如何扩容的呢?
以下分析基于JDK1.7版本。
1.当你new一个集合时,ArrayList<String> list = new ArrayList<String>();
private static final Object[] EMPTY_ELEMENTDATA = {}; private transient Object[] elementData; //构造函数 public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
分析:当你新建一个新的 ArrayList 对象时,默认新建一个空的Object[]类型的数组。
2.集合添加元素 list .add("666")。
//定义size private int size; //默认数组大小 10 private static final int DEFAULT_CAPACITY = 10; public boolean add(E e) { // ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
分析:第一次添加时size = 0,然后添加之后长度 size + 1, 需要判断数组是否需要扩容,ensureCapacityInternal(size+1)
private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { //如果数组为空,则直接把数组大小设为默认长度10 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) //如果添加元素之后的数组长度大于当前数组的最大长度,则进行扩容 grow(minCapacity); } /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //MAX_VALUE = 0x7fffffff; /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { //扩容 // overflow-conscious code int oldCapacity = elementData.length; //记住原数组大小 int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组大小为,就数组大小 加上 旧数组右大小移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); //把旧数组的内容copy到新数组中 } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }分析:每一次添加前都会判断添加之后的长度是否大于了当前数组的最大长度。
当第一次添加时,数组长度默认设为10。
以后如果数组存满,则扩容的机制是oldCapacity + (oldCapacity >> 1)
新数组长度 = 原数组长度 + 原数组长度右移1位后的值。
举例: 10 ---》 10 + 5 ---》 15 + 7 ---》 22 + 11 ---》 33 + 16 = 49 。。。
其实也就是1.5倍扩容,因为右移一位相当于除以2。
3.集合添加元素 list .remove(index)。
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); elementData[--size] = null; // clear to let GC do its work return oldValue; } //
public boolean remove(Object o) { if (o == null) { //如果要删除的内容为null,则遍历数组进行删除 for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { //如果不为null,则遍历找到对应的内容进行删除 for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
private void fastRemove(int index) { //快速删除,和根据索引删除的区别是,不返回被删除的内容 modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }分析:remove有三个,一个是根据索引删除,一个是根据内容删除,一个是快速删除。
1.根据索引删除的原理是:把要删除索引内容后的元素整体向前移动一位,并把数组最后一位置为null,并且返回被删除的内容
2.fastRemove和remove(int index)区别就是,后者把被删除的内容给返回,而前者没有。
3.remove(Object o)则会遍历数组进行调用fastRemove,进行删除。即使数组中有重复的,也只会删除第一个。