本文结构:
1.介绍特点
2.基本方法
3.重点源码分析
1.介绍特点
ArrayList:
是List的一个具体实现子类,是List接口的一个数组实现 (里面必定维护了一个数组)。
默认初始容量10, 扩容机制1.5倍。(数组必然有初始容量和扩容机制)
有序。
允许null元素。
允许重复元素。
线程不安全。
2.基本方法
关键字 | 简介 |
---|---|
| 增加 |
| 判断是否存在 |
| 获取指定位置的对象 |
| 获取对象所处的位置 |
| 删除 |
| 替换 |
| 获取大小 |
| 转换为数组 |
| 把另一个容器所有对象都加进来 |
| 清空 |
3.重点源码分析
源码解析:
import java.util.Arrays;
import java.util.ConcurrentModificationException;
public class MyArrayList<E>{
private static final int DEFAULT_CAPACITY = 10;// 默认容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA ={} ;//空数组
private Object[] elements;//底层维护的数组
private int size;//容器内对象的个数
private int modCount;//记录ArrayList这个对象被修改的次数
//构造方法
public MyArrayList() {
this.elements = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//获取列表存的数据量
public int getSize() {
return size;
}
/**
*增加内容
* @param e
* @return
*/
public boolean add(E e) {
//创建一个默认为10的数组,若小则扩容
ensureCapacityInternal(size+1);
//确保容量够,则添加数据,并讲数据大小加1.
elements[size]=e;
size++;
return true;
}
//该方法来确保数组的容量够用,若为创建则创建数组并赋予默认值。
private void ensureCapacityInternal(int minCapacity) {
//若数组为空(还未添加数据)
if (elements==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
//则选出默认值和最小容量的最大值
minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);
//只add方法其实没必要比较,主要是addAll()这个方法需要比较
}
modCount++;//修改次数加一
//判断一下是否需要扩容,若数据+1大于当前数组,则需要扩容
if(minCapacity-elements.length>0){
grow(minCapacity);//调用扩容方法
}
}
/**
* 具体的扩容方法
* @param minCapacity
*/
private void grow(int minCapacity){
int oldCapacity=elements.length;//获取原始数组的长度
int newCapacity=oldCapacity+(oldCapacity>>1);//扩容 1+0.5 倍
//若扩容后的长度比所需要的最低长度还要小,则直接把扩容的长度更改为最低所需长度
if (newCapacity-minCapacity<0){
newCapacity=minCapacity;
}
//若扩容完的新长度比规定的最大容量还大,则要进一步判断,并进一步修改数组大小
if (newCapacity-MAX_ARRAY_SIZE>0){
//若所需的容量竟然小于0,说明超过Int最大值,越界了,抛出异常
if (minCapacity<0){
throw new OutOfMemoryError();
}
//若所需的容量不超过Int最大值,则再判断
if (minCapacity>MAX_ARRAY_SIZE){//若所需的大于数组要求的而小于Int最大值
newCapacity=Integer.MAX_VALUE;//直接赋值Int最大值
}else{//否则只有小于数组最大值这一种情况了,赋予数组最大值即可
newCapacity=MAX_ARRAY_SIZE;
}
}
elements = new Object[newCapacity];
}
/**
* 根据下标添加元素
* @param index 下标
* @param element 要替换的元素
*/
public void add(int index,E element){
//先检查一下下标是否越界
rangeCheck(index);
//确保数组长度够用
ensureCapacityInternal(size+1);
//这里调用System里面一个静态的本地方法(效率更高),用于数组的复制。这里把下标后面的都向后移了。
/* public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
Object src:the source array. 源数组
int srcPos:starting position in the source array. 在源数组中,开始复制的位置
Object dest:the destination array. 目标数组
int destPos:starting position in the destination data. 在目标数组中,开始赋值的位置
int length:the number of array elements to be copied. 被复制的数组元素的数量*/
System.arraycopy(element, index, element, index + 1, size - index);//等于说把下标后面的都向后移了。
//原数组 开始的位置 目标数组 目标数组中开始复制的位置 被复制的数量
//由于下标后的都往后移动了一位,因此index的位置为空,插进去即可
elements[index]=element;
size++;
}
/**
* 检查下标是否越界
* @param index
*/
private void rangeCheck(int index) {
if (index>size||index<0){//若插入的下标比数据实际数量大或者输入的是个不合常理的负数
throw new IndexOutOfBoundsException((outOfBoundsMsg(index)));//抛出异常并返回要插入的下标数和实际数据的数量
}
}
/**
* 用于越界异常的信息抛出
* @param index 输入下标
* @return 返回下标和实际数据的数量大小
*/
private String outOfBoundsMsg(int index) {
return "Index:"+index+",Size:"+size;
}
/**
* 清空列表所有元素
* 由垃圾回收器(GC)进行回收
*/
public void clear(){
//记录对象修改次数
modCount++;
//让数组所有的数据都变为null, clear to let GC do its work
for (int i=0;i<size;i++){
elements[i]=null;
}
//这时长度都为0了
size=0;
}
/**
* 根据下标更改数据
* @param index 下标
* @param element 想要更改的数据
* @return 返回原始数据
*/
public E set(int index, E element) {
//首先判断是否越界
rangeCheck(index);
E oldValue= (E) elements[index];
elements[index]=element;
return oldValue;
}
/**
* 检查操作数是否一致,防止并发修改错误
*//*
private void checkForComodification() {
if (MyArrayList.this.modCount != this.modCount)//线程中的和保存的数据不一样
throw new ConcurrentModificationException();//不一致则抛出异常
}*/
/**
* 简简单单的根据下标查询数据操作
* @param index 下标
* @return 返回数据信息
*/
public E get(int index) {
rangeCheck(index);
return (E) elements[index];
}
/**
* 根据下标删除元素
* @param index 要删除的下标
* @return 返回要删除的数据
*/
public E remove (int index){
rangeCheck(index);//检查一下下标是否合适
modCount++;//操作数加一
E oldValue= (E) elements[index];
//定义numMoved,记录一下index后面的数,等会要把他们都移动了
int numMoved=size-index-1;
if (numMoved>0){
System.arraycopy(elements,index+1,elements,index,numMoved);//通过本地静态的复制方法,来实现数据的前移。
}
elements[size]=null;//都前移了以后原本最后一位数的内存设为null,让GC回收了 clear to let GC do its work
size--;
return oldValue;
}
/**
* 找到相应的元素并对其进行删除
* @param o
* @return 返回true表示删除完成,false表示删除失败
*/
public boolean remove(Object o) {
if (o == null) {//删除null数据
for (int index = 0; index < size; index++)
if (elements[index] == null) {
fastRemove(index);
return true;
}
} else {//删除正常的数据
for (int index = 0; index < size; index++)
if (o.equals(elements[index])) {
fastRemove(index);
return true;
}
}
return false;//若没找到该数据则返回false
}
/**
* 无需进行判断下标是否合适的快速删除方法
* @param index
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elements, index+1, elements, index,
numMoved);
elements[--size] = null; // clear to let GC do its work
}
/**
* 判断数据是否为空(列表里没数据)
* @return 若为空则返回true
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 查询是否有该数据
* @param o
* @return 若有返回true
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;//若该索引为正,则表明有该数据,且第一次出现的位置在indexOf
}
/**
*返回指定元素的第一次出现的索引
* @param o
* @return 若存在则返回第一次出现的位置,若没有则返回-1
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elements[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
/**
*返回指定元素的最后一次出现的索引
* @param o
* @return 若存在则返回最后一次出现的位置,若没有则返回-1
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)//从后向前遍历即可
if (elements[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elements[i]))
return i;
}
return -1;
}
@Override
public String toString() {
return "MyArrayList{" +
"elements=" + Arrays.toString(elements) +
'}';
}
}