ArrayList集合的特点:
- 低层是使用数组结构来实现的
- 适合轮询和遍历
- 它是非同步的,效率高,多个线程同时操作时,会造成安全问题
因为ArrayList是支持泛型的,也就是说除非基本数据类型,其他的数据类型都可以存储(引用数据类型)
那么我们也写一个支持泛型的类
public class MyArrayList<T> {
private Object[] elementData; // 存储数据
private final int DEFUALT_CAPACITY = 10; // 数组初始化容量为10
private int size; // 记录实际数组的长度
//====================构造方法=========================
public MyArrayList(){
// 初始化容量为10
elementData = new Object[DEFUALT_CAPACITY];
}
public MyArrayList(int capacity) {
// 初始化指定容量
if (capacity > 0) {
this.elementData = new Object[capacity];
} else if (capacity == 0) {
this.elementData = new Object[DEFUALT_CAPACITY];
} else {
throw new IllegalArgumentException("Illegal Capacity: " + capacity);
}
}
}
简单的实现几个方法
add方法
/**
* 把指定数据添加到数组中
* @param element 数据
* @return boolean
*/
public boolean add(T element) {
// 要先知道数组还有没有空间存储要添加的元素
// size + 1就是我想要的最小容量,所以需要判断当前element数组的长度是否大于最小容量的长度
// 大于的话在进行添加操作,反之需要对数组进行扩容
// 保证容量空间足够添加一个元素
ensureCapacitySpace(size + 1);
// 先给elementData[size]赋值,size再 + 1
elementData[size++] = element;
return true;
}
/**
* 判断element数组的容量长度是否符合最小容量长度
* @param minCapacity 最小容量长度
*/
private void ensureCapacitySpace(int minCapacity) {
if(elementData.length - minCapacity < 0) {
// 说明不满足最小容量
// 需要对数组扩容
grow(minCapacity);
}
}
/**
* 计算一个合理的扩容长度
* @param minCapacity 最小容量
*/
private void grow(int minCapacity) {
// 对数组扩容之前,需要知道给数组扩容到多少合适
// 源码写的是1.5倍,那么我们也写1.5倍吧
// >>是位运算,相当于除2,比普通的运算符效率高
int newCapacity = (elementData.length >> 1) + elementData.length;
// 已经得到合理的容量,再进行和最小容量比较
if(newCapacity - minCapacity < 0) {
// 依然不小于最小容量,那么就直接用最小容量
newCapacity = minCapacity;
}
// 已经得到了合理的容量,可以对数组进行扩容
// 第一种方法:Arrays.copyOf();
// elementData = Arrays.copyOf(elementData, newCapacity);
// 第二种方法:也可以自己写一个方法来实现对数组的扩容
copyOf(newCapacity);
}
/**
* 扩容
* @param newCapacity 最小容量
*/
private void copyOf(int newCapacity) {
// 创建一个newCapacity长度的数组
Object[] newElementData = new Object[newCapacity];
// 把原数组中所有的元素都复制到新的数组中
for(int i = 0; i < elementData.length; i++) {
newElementData[i] = elementData[i];
}
// 然后再把新的数组给原数组
elementData = newElementData;
}
length方法
/**
* 返回实际容量的长度
* @return int类型容量长度
*/
public int size() {
return this.size;
}
get方法
/**
* 返回指定索引位置上的元素
* @param index 索引
* @return 元素
*/
public T get(int index) {
// 需要知道index索引符合实际容量长度的范围之内 --> 0至(size - 1)
rangeCheck(index);
// index是符合条件的,返回索引对应的元素
return (T) elementData[index];
}
/**
* 判断index是否在范围内
* @param index 索引
*/
private void rangeCheck(int index) {
if(index < 0 || index >= this.size) {
throw new IndexOutOfBoundsException("beyond range: " + index);
}
}
remove方法
/**
* 获取移除指定索引位置上的元素
* @param index 索引
* @return 被移除元素
*/
public T remove(int index) {
// 这里也需要对index索引进行判断,所以直接调用rangeCheck方法就行啦
rangeCheck(index);
// 进行移除操作
return removeOneElement(index);
}
/**
* 移除元素
* @param index 元素
* @return 被移除的元素
*/
private T removeOneElement(int index) {
// 先要得到移除的元素
T oldValue = (T) elementData[index];
// 第一种方法实现
// 把index后面每一个元素都向前移一位
// for(int i = index; i < size - 1; i++) {
// elementData[i] = elementData[i + 1];
// }
// 因为移除了一个元素,那么size就要相应的-1,并把最后一位元素设为null
// elementData[--size] = null;
//=============================================
// 第二种方法实现
Object[] newElementData = new Object[--size];
for(int i = 0; i < size; i++) {
if( i >= index) {
newElementData[i] = elementData[i + 1];
}
newElementData[i] = elementData[i];
}
elementData = newElementData;
return oldValue;
}
set方法
/**
* 修改元素
* @param index 索引
* @param t 新元素
* @return 被修改之前的元素
*/
public T set(int index, T t) {
// 也是首先要判断index是否在合理的范围内
rangeCheck(index);
// 进行修改操作
return updateOneElement(index, t);
}
private T updateOneElement(int index, T t) {
// 修改之前要先保存旧的元素
T oldElement = (T) elementData[index];
// 覆盖
elementData[index] = t;
return oldElement;
}
isEmpty方法
/**
* 当前列表是否为空
* @return boolean
*/
public boolean isEmpty() {
return size == 0;
}
indexOf方法
/**
* 返回首次出现指定对象相同的元素对应的索引
* @param o 对象
* @return 索引
*/
public int indexOf(Object o) {
// o如果为空的话,就比较引用
if(o == null) {
for(int i = 0; i < size; i++) {
if(elementData[i] == null) {
return i;
}
}
}else {
// o如果不为空,比较equals
for(int i = 0; i < size; i++) {
// 为什么不写成elementData[i].equals(o)?
// 因为elementData内可以会有空值,会造成空指针异常
// 而o这个对象,已经经过判断了,o不可以为空
if(o.equals(elementData[i])) {
return i;
}
}
}
// 没有相等的值,返回-1
return -1;
}
contains方法
/**
* 判断是否包含指定的对象
* @param o 对象
* @return boolean
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
clear方法
/**
* 移除所有的元素
*/
public void clear() {
// 把数组中每一个元素都设为null
for(int i = 0; i < size; i++) {
elementData[i] = null;
}
// size设为0
size = 0;
}