手写ArrayList源代码
我们为什么要使用集合collection,因为仅仅使用数组不能满足日常生活中的需求,数组固定数据类型的,不可变长度的,许多操作数组的方法需要我们自己写出来,很不利于编程使用,复用性很差。在集合中有很多种自带的方法,帮助我们完成数据的增删改查。
java中集合的总接口是Collection.java中所有和集合有关的内容,都是collection的接口或者子接口,或者实现类
interface Collection
–| interface List List接口,有序可重复
----| class ArrayList
【重点】可变长数组结构
原码实现,了解其中的特征,性能…
----| class LinkedList
【重点】双向链表结构
----| class Vector
【远古时代】JDK1.0 线程安全的ArrayList,如果不考虑线程安全问
题,建议使用ArrayList
–| interface Set Set接口,无序不可重复
----| HashSet 底层存储数据的方式是采用哈希表方式
----| TreeSet 底层存储数据的方式一个平衡二叉树方式
自己完成ArrayList里面的基本方法
ArrayList里面的成员变量和构造方法
/**
* 准备一个底层数组,用于存储数据内容
*/
private Object[] elements;
/**
* 初始化默认容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 最大数组容量, -8是为了腾出一定的空间,保存数组的必要内容
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 当前底层数组中保存的有效元素个数
*/
private int size = 0;
/**
* 无参数构造方法,但是需要提供给用户一个初始化容量来保存必要的数据
*/
public MyArrayList() {
/*
* 使用this关键字调用类内的其他构造方法 、
* 根据实际参数里类型来选对应对应的构造方法,必须在当前构造
* 方法代码块的第一行
*/
this(DEFAULT_CAPACITY);
}
/**
* 用户指定保存元素容量的初始化过程,要求用户指定的容量范围是有效的
*
* @param initCapacity 用户指定的初始化容量,但是不能小于等于0 ,不能大于 MAX_ARRAY_SIZE
*/
public MyArrayList(int initCapacity) {
// 用户传入参数的合法性判断过程
if (initCapacity < 0 || initCapacity > MAX_ARRAY_SIZE) {
// 抛出异常
// IllegalArgumentException 是一个RuntimeException运行时异常的子类
// 不需要强制声明抛出异常
throw new IllegalArgumentException("IllegalArgumentException : " + initCapacity);
}
elements = new Object[initCapacity];
增加操作
/*
* 增加方法
*/
/**
* 添加元素到当前集合的末尾
*
* @param e 要求是符合泛型约束的指定数据类型
* @return 添加成功返回true, 否则返回false
*/
public boolean add(E e) {
// 直接调用在指定下标位置添加元素的方法,只不过这里指定下标位置就是
// 尾插法下标位置
return add(size, e);
}
/**
* 在底层数组的指定下标位置保存对应的元素
*
* @param index 指定下标位置,不能超出有效范围,0<= index <= size
* @param e 符合泛型约束的数据类型
* @return 添加成功返回true, 否则返回false
*/
public boolean add(int index, E e) {
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException(index);
}
ensureCapacity(size + 1);
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = e;
size += 1;
return true;
}
/*
* addAll方法 1. 需要得到添加集合中元素内容,有效元素个数 2. 确认容量问题 3. size = srcSize + newSize
*/
/**
* 添加另一个集合到当前集合的末尾
*
* @param list MyArrayList类型,自定义ArrayList,要求存储元素和当前集合一致,或者 是其子类
* @return 添加成功返回true,添加失败返回false
*/
public boolean addAll(MyArrayList<? extends E> list) {
Object[] array = list.toArray();
int newSize = array.length;
ensureCapacity(size + newSize);
for (int i = 0; i < newSize; i++) {
elements[i + size] = array[i];
}
size += newSize;
return true;
}
/**
* 添加另一个符合当前集合要求的List,到当前集合内
*
* @param index 指定插入的下标位置
* @param list 符合当前添加要求的另一个MyArrayList集合
* @return 添加成功返回true
*/
public boolean addAll(int index, MyArrayList<? extends E> list) {
// 用户指定的下标判断
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException(index);
}
Object[] array = list.toArray();
int newSize = array.length;
ensureCapacity(size + newSize);
// 移动操作
for (int i = size - 1; i >= index; i--) {
elements[i + newSize] = elements[i];
}
// 存入另一个集合中的元素
for (int i = index; i < index + newSize; i++) {
elements[i] = array[i - index];
}
// 有效元素个数需要修改
size += newSize;
return true;
}
删除方法
/**
* 删除指定元素
*
* @param obj 指定删除的元素
* @return 删除成功返回true
*/
public boolean remove(Object obj) {
int index = indexOf(obj);
return null != remove(index);
}
/**
* 删除下标元素
*
* @param index 指定的下标范围
* @return 删除成功返回对应元素,失败返回null
*/
public E remove(int index) {
if (-1 == index) {
return null;
}
E e = get(index);
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
// 原本最后一个有效元素位置上的内容赋值为null
elements[size - 1] = null;
size -= 1;
return e;
}
修改的方法
/**
* 替换指定下标的元素
*
* @param index 指定下标元素,但是必须在有效范围以内
* @param e 符合泛型约束的对应数据类型
* @return 被替换的元素
*/
public E set(int index, E e) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException(index);
}
E temp = get(index);
elements[index] = e;
return temp;
}
其他需要使用的方法
/**
* 判断指定元素是否存在
*
* @param obj 指定元素
* @return 存在返回true,不存在返回false
*/
public boolean contains(Object obj) {
return indexOf(obj) > -1;
}
/**
* 作业 情况1
* 集合1 {1, 3, 5, 7, 9}
* 集合2 {3, 5, 7} 子集合!!True
* 集合3 {3, 7, 5} 不是!!! false
*
* 情况2
* 集合1 {1, 3, 3, 5, 9, 3, 5, 7, 9}
* 集合2 {3, 5} true
* 集合3 {5, 3}
* 集合4 {1, 3, 6, 7, 9, 3, 5, 7, 9} true
*
* @param list
* @return
*/
public boolean containsAll(MyArrayList<?> list) {
// 判断list是否有内容 已经当前list对应的地址是不是null
if (null == list || list.isEmpty()) {
throw new NullPointerException();
}
// 1. 计数器 找出list参数集合中下标为0的元素在集合中出现的位置次数
int count = 0;
boolean flag = true;
// 2. 存储list参数集合中下班未0的元素出现的位置
int[] indexArr = new int[this.size];
for (int i = 0; i < this.size; i++) {
// 在当前集合中下标为i的元素和list参数集合中下标为0的元素比较
if (this.get(i).equals(list.get(0))) {
indexArr[count] = i;
count += 1;
}
}
// 4. 判断是否存list.get(0)的元素
if (0 == count) {
return false;
}
// 5. 进入循环,开始匹配,从找到的所有list[0]匹配
for (int i = 0; i < count; i++) {
// 6. 遍历操作当前集合
// 从当前集合中对应查询位置 + 1开始,循环次数是list.size() - 1
for (int j = indexArr[i] + 1; j < indexArr[i] + list.size(); j++) {
// list从下标1开始获取元素
int index = 1;
if (!this.get(j).equals(list.get(index++))) {
flag = false;
break;
}
flag = true;
}
if (flag) {
break;
}
}
return flag;
}
/**
* 判断集合是否是空的
*
* @return 如果为空,返回true, 否则返回false
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 获取当前集合中有效元素个数
*
* @return 有效元素个数
*/
public int size() {
return size;
}
/**
* 获取当前集合的子集合,截取范围是fromIndex <= n < endIndex
*
* @param fromIndex fromIndex <= endIndex 不得小于0
* @param endIndex endIndex >= fromIndex 小于等于size
* @return 截取得到的一个MyArrayList子集合对象
*/
public MyArrayList<E> subList(int fromIndex, int endIndex) {
if (fromIndex > endIndex || fromIndex < 0 || endIndex > size) {
throw new ArrayIndexOutOfBoundsException();
}
MyArrayList<E> listTemp = new MyArrayList<E>(endIndex - fromIndex);
for (int i = fromIndex; i < endIndex; i++) {
listTemp.add(this.get(i));
}
return listTemp;
}
@Override
public String toString() {
if (isEmpty()) {
return "[]";
}
String str = "[";
for (int i = 0; i < size - 1; i++) {
str += elements[i] + ", ";
}
return str + elements[size - 1] + "]";
}
/*
* 这里需要类内使用的可以用于判定当前容量是否满足添加要求的方法 如果满足直接进入添加模式,如果不满足,需要执行grow方法,完成 底层数组的扩容问题。
*/
/**
* 每一次添加元素,都需要进行容量判断,如果满足可以进行添加操作 不满足需要制定grow方法
*
* @param minCapacity 要求的最小容量
*/
private void ensureCapacity(int minCapacity) {
if (minCapacity > elements.length) {
// 完成一个底层数组的扩容方法
grow(minCapacity);
}
}
/**
* 底层数组的扩容方法,原理是创建新数组,移植数据,保存新数组地址
*
* @param minCapacity 要求的最小容量
*/
private void grow(int minCapacity) {
// 1. 获取原数组容量
int oldCapacity = elements.length;
// 2. 计算得到新数组容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 3. 判断新数组容量是否满足要求
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
// 新数组容量是大于允许的最大数组容量
if (newCapacity > MAX_ARRAY_SIZE) {
// 二次判断minCapacity是否小于MAX_ARRAY_SIZE
if (minCapacity < MAX_ARRAY_SIZE) {
// 最小要求是不大于MAX_ARRAY_SIZE,代码可以运行
newCapacity = minCapacity;
} else {
throw new OutOfMemoryError("Overflow MAX_ARRAY_SIZE");
}
}
/*
* 4. 使用数组工具类方法完成操作 Arrays.copyOf(源数据数组,可以是任意类型,采用泛型约束, 指定的新数组容量); a.
* 根据指定的新数组容量创建对应泛型数据类型的新数组 b. 从源数据数组中拷贝内容到新数组中 c. 返回新数组首地址
*/
elements = Arrays.copyOf(elements, newCapacity);
}