最近学习了ArrayList类,自己试着编写了其中常用的函数,发现和源码差的不是一点点...
在类的属性中,定义了size属性,记录数组的长度。以及elementData空数组
/* 获得数组的长度 */
public int size() {
return size;
}
/* 判断数组是否为空 */
public boolean isEmpty() {
return size == 0;
}
/* 获得元素的索引位 */
public int indexOf(Object o) { //返回传入参数的索引位
if (o == null) { //当传入参数为空时,遍历数组是否有元素为空
for (int i = 0; i < size; i++) //防止equals函数报错
if (elementData[i]==null)
return i; //返回位置
} else { //传入参数不为空时遍历数组,找到匹配元素
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i; //相等则返回位置
}
return -1; //找不到则返回-1
}
/* 获得元素最后一次出现的索引位 */
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--) //从数组的最后一个开始向前遍历
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/* 返回指定索引位的元素 */
public E get(int index) { //利用了泛型,输入索引位返回值
rangeCheck(index); //该函数用来判断是否下标越界
return elementData(index); //下标不越界则返回值(该函数将取到的数据强制转化为泛型)
}
private void rangeCheck(int index) { //如果参数大于数组的大小,抛出异常
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
/* 更改指定索引位的数据,并返回原值 */
public E set(int index, E element) { //设置索引位index的数据值为element
rangeCheck(index); //检查索引是否越界
E oldValue = elementData(index); //将原值强制转换为泛型
elementData[index] = element; //赋值
return oldValue; //返回原值
}
/* 向数组尾添加元素 */
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 将最小容量设置为当前长度加1,便于之后扩容
elementData[size++] = e; //扩容之后将元素添加到末尾即可
return true; //返回添加成功
}
private void ensureCapacityInternal(int minCapacity) { //确保内部容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //如果数组现在是空的话
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //最小值为默认容量和最小容量的最大值(保证数组至少是默认容量大小)
} //minCapacity表示所需的最小容量
ensureExplicitCapacity(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;
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) { //设置最大容量函数
if (minCapacity < 0) // overflow //如果最小容量小于0,抛出溢出错误
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //如果最小容量大于最大数组容量,则返回整数的最大值
Integer.MAX_VALUE : //否则返回设定的最大数组容量
MAX_ARRAY_SIZE;
}
/* 向指定索引位添加元素 */
public void add(int index, E element) {
rangeCheckForAdd(index); //为了数组添加的越界检查
ensureCapacityInternal(size + 1); // Increments modCount!! 与上一个add相同,数组扩容
System.arraycopy(elementData, index, elementData, index + 1,size - index); //将索引位前后分别复制到一个新数组里,留下索引位空位
elementData[index] = element; //向索引位加入元素
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0) //如果索引位大于数组长度或者小于0,抛出索引越界异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(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 清空最后一个空位,让GC回收
return oldValue;
}
/* 移除数组里的某个元素 removeAll方法需要传入一个集合对象 */
public boolean remove(Object o) {
if (o == null) { //如果要移除的数据为空的话
for (int index = 0; index < size; index++) //遍历数组,如果有为空的元素,则移除该元素
if (elementData[index] == null) {
fastRemove(index); //与上remove操作类似
return true;
}
} else { //如果要移除的数据不是空
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
}
/* 清空数组 */
public void clear() {
modCount++; //修改次数增加
// clear to let GC do its work
for (int i = 0; i < size; i++) //遍历数组均置为空,让GC回收
elementData[i] = null;
size = 0; //数组尺寸设为0
}
/* 添加集合中的全部元素到数组尾 */
public boolean addAll(Collection<? extends E> c) { //泛型
Object[] a = c.toArray(); //将集合c转换为数组a
int numNew = a.length; //获得a的长度
ensureCapacityInternal(size + numNew); // Increments modCount 将数组长度至少设置为size+numNew便于扩容
System.arraycopy(a, 0, elementData, size, numNew); //将一个新长度的数组赋给原数组
size += numNew; //数组有效尺寸增加
return numNew != 0;
}
/* 在指定索引位增加集合 */
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index); //为添加检查索引位是否越界
Object[] a = c.toArray();
int numNew = a.length; //与上操作相同
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index; //获得需要移动的数据个数
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew, //重新获得数组,留出中间的空位
numMoved);
System.arraycopy(a, 0, elementData, index, numNew); //将a数组赋值到空位中
size += numNew; //数组有效位数增加
return numNew != 0;
}
/* 将数组两个索引位之间的数据取出为List形式
注意:subList包前不包后,截取的是fromIndex到toIndex之前的部分
*/
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size); //检查子链的两个索引位是否符合要求
return new SubList(this, 0, fromIndex, toIndex); //返回从fromIndex到toIndex的子链 SubList是一个内部类
}
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0) //第一个索引位小于0则抛出异常
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size) //第二个索引位大于数组尺寸则抛出异常
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex) //第一个索引位大于第二个索引位抛出异常
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
/*
*在这个函数中,操作的还是原来的集合,即对截取出来的子集进行处理时,原集合也会改变。虽然子集
*与子集的地址不是同一个,但是指向的是同一个对象
*/