ArrayList整理
- 1,ArrayList特性
- 2,ArrayList底层实现的特征
- 1),ArrayList初始化
- 2),初始容量
- 3),ArrayList的添加元素的add()方法
- 4),ArrayList的删除方法remove(int index)
- 其他的一些方法的操作其实都差不多,所以就不过多累述了。
1,ArrayList特性
ArrayList底层是动态数组,所以根据数组的特性可知:
//可知ArrayList底层实际就是一个数据,通过transient修饰,标识不可以被序列化
transient Object[] elementData;
1),ArrayList中存储元素位置的地址是一串连续的地址值。2),由于数组有索引,所以ArrayList对于元素的查找与修改效率快;增加与删除效率低,因为需要移动目标元素之后的所有元素。3),但是在增加元素到最后一个位置与删除最后一个元素效率是高的。
2,ArrayList底层实现的特征
1),ArrayList初始化
//先定义了两个个空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//再来看看ArrayList的三个构造函数
//1,自己传入数组的初始容量(initialCapacity)
public ArrayList(int initialCapacity) {//判断我们传入的初始容量是否大于0if (initialCapacity > 0) {//如果大于0,则直接使用new关键字创建长度为initialCapacity的数组,并将elementData指向该数组this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {//如果等于0,则将elementData指向之前定义的空数组this.elementData = EMPTY_ELEMENTDATA;} else {//小于0的话就抛出一个非法参数异常throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}
}
//2,无参构造器
public ArrayList() {//如果是无参构造器的话,就直接将elementData指向之前定义的空数组(注意,没看错,就是空)this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//3,传入一个列表,根据传入的列表创建一个ArrayList列表
public ArrayList(Collection<? extends E> c) {//首相我们看到传入的集合调用了toArray()方法(将列表转化成数组)//c.toArray();返回的是一个Object[]elementData = c.toArray();if ((size = elementData.length) != 0) {if (elementData.getClass() != Object[].class)//如果elementData不是一个Object[],那么就将elementData中的所有//元素都复制到一个Object[]中,再将该数组给elementDataelementData = Arrays.copyOf(elementData, size, Object[].class);} else {//如果传入集合的长度是0,那么就将elementData指向空数组this.elementData = EMPTY_ELEMENTDATA;}
}
2),初始容量
//从这一段代码可知ArrayList的初始容量是10
//但是默认初始化的时候却是一空数组,所以初始化还没结束,接下来的add方法我们回提到
private static final int DEFAULT_CAPACITY = 10;
3),ArrayList的添加元素的add()方法
//向ArrayList中添加元素
public boolean add(E e) {//调用了一个ensureCapacityInternal方法,参数是size+1(size就是数组中元素的多少,不是数组长度,默认是0)ensureCapacityInternal(size + 1);//这时如果需要扩容的话就已经做好了扩容,直接赋值,不需要扩容就直接赋值elementData[size++] = e;return true;
}
//进入到ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {//首先判断了一下elementData是否是一个空数组,也就是第一次添加元素的时候对数组进行初始化(长度是10)if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果是的话,那么将DEFAULT_CAPACITY(先前提到,这就是默认长度10)与传入的参数(size+1)中的最大值//给minCapacityminCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//这里又调用了ensureExplicitCapacity方法,参数是刚刚取得的最大值ensureExplicitCapacity(minCapacity);
}
//进入ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {//将modCount自增modCount++;//如果minCapacity大于数组的长度,说明我们这次添加元素会导致超出数组的长度//那么可知,原来这一串的方法都是为了一件事,那就是判断是否需要扩容if (minCapacity - elementData.length > 0)//如果需要扩容的话,那么就调用grow方法,参数是minCapacitygrow(minCapacity);
}
//进入grow方法
private void grow(int minCapacity) {// 首先获得数组的长度int oldCapacity = elementData.length;//新数组的长度是旧数组长度的2倍(右移2位标识原来的0.5)int newCapacity = oldCapacity + (oldCapacity >> 1);//如果计算出来的新数组长度小于minCapacity(即添加元素后数组的长度,size+1或者默认为10)if (newCapacity - minCapacity < 0)//则将minCapacity作为新数组长度//这一步主要就是调用无参构造时,默认长度是10,但是在初始化的时候数组还是0//所以在添加第一个元素的时候将数组进行初始化,即数组的长度是10newCapacity = minCapacity;//如果新数组的长度大于设置的最大数组长度if (newCapacity - MAX_ARRAY_SIZE > 0)//进入hugeCapacity方法newCapacity = hugeCapacity(minCapacity);// 使用Arrays.copyOf()方法将数组进行扩容,并将就数组中的元素全部复制到新数组中elementData = Arrays.copyOf(elementData, newCapacity);
}
//hugeCapacity方法
private static int hugeCapacity(int minCapacity) {//如果小于0则直接抛出异常if (minCapacity < 0) // overflowthrow new OutOfMemoryError();//如果大于最大设置的长度,则将int的最大值给它return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}
4),ArrayList的删除方法remove(int index)
//删除指定位置元素
public E remove(int index) {//首先调用rangeCheck,作用是判断传入的index是否合法rangeCheck(index);//modCount自增modCount++;//将该位置的元素找出E oldValue = elementData(index);//判断index所在的位置是否是最后一位int numMoved = size - index - 1;if (numMoved > 0)//如果index不在最后一位//则使用System.arraycopy()方法将index之后的所有元素赋值到index位置(从index位置开始)//例如:2,3,4,5 其中index = 1;//那么:使用System.arraycopy()后为 2,4,5,5System.arraycopy(elementData, index+1, elementData, index,numMoved);//将最后一位设置为nullelementData[--size] = null; // clear to let GC do its workreturn oldValue;
}