模拟实现ArrayList
ArrayList
是Java集合框架(java.util
包)的一部分,是一个基于数组实现的列表类,提供了动态数组的功能。与Java的普通数组相比,ArrayList
的容量能够自动增长,这使得使用者无需在使用前确定数组的大小。
一. 模拟实现ArrayList
我们这里是模拟实现,所以实现基本功能即可
# 1.定义顺序顺序表
elem
:一个整型数组,用于存储MyArrayList
中的元素。usedSize
:表示MyArrayList
当前存储的元素个数。DEFAULT_SIZE
:MyArrayList
的默认容量,用于在构造方法中初始化数组的大小。- 构造方法
MyArrayList()
:创建一个默认容量为DEFAULT_SIZE
的数组,并将usedSize
初始化为0。
public class MyArrayList {
public int[] elem; // 存储元素的数组
public int usedSize; // 当前数组中包含的元素个数
private static final int DEFAULT_SIZE = 10; // 默认数组容量
// 构造方法,初始化数组容量为DEFAULT_SIZE
public MyArrayList() {
this.elem = new int[DEFAULT_SIZE];
this.usedSize = 0; // 初始化时,顺序表中元素个数为0
}
}
2
3
4
5
6
7
8
9
10
11
12
# 2. 函数实现
# 2.1 display(打印)
打印函数顾名思义就是将顺序表中所有的数据打印出来。打印完的标准就是将顺序表中的数组完全输出,因此用usedSize作为终止条件,
public void display() {
// 遍历数组中的每个元素,直到usedSize
for (int i = 0; i < this.usedSize; i++) {
// 打印当前元素并用空格隔开
System.out.print(this.elem[i] + " ");
}
// 所有元素打印完毕后换行
System.out.println();
}
2
3
4
5
6
7
8
9
# 2.2 add(新增)
add
方法是用于向MyArrayList
顺序表中添加一个新元素的。这个方法首先检查当前顺序表的存储空间是否已满,如果已满,则进行扩容操作;如果未满,直接将新元素添加到顺序表的末尾。
public void add(int data) {
// 判断当前数组是否已满,即元素个数等于数组长度
if (this.usedSize == elem.length) {
// 如果数组已满,则对数组进行扩容。新数组长度为原数组长度的2倍
// 使用Arrays.copyOf方法实现数组扩容,并将扩容后的新数组赋值给elem
this.elem = Arrays.copyOf(elem, elem.length * 2);
}
// 在数组未满的情况下,或扩容后,将新数据添加到数组的usedSize位置
// usedSize表示当前存储的元素个数,同时也指向了下一个可以存放元素的位置
elem[usedSize] = data;
// 添加元素后,元素个数usedSize增加1
usedSize++;
}
2
3
4
5
6
7
8
9
10
11
12
13
# 2.3 add(指定位置新增)
这个add
方法的作用是在MyArrayList
顺序表的指定位置pos
插入一个新的元素data
。这个过程需要考虑几个关键步骤:下标合法性检查、顺序表空间检查、元素后移以及实际插入操作
public void add(int pos, int data) {
// 检查指定的位置pos是否合法
// pos的合法范围是0到usedSize,包含0和usedSize,因为可以在末尾添加元素
if (pos < 0 || pos > usedSize) {
throw new ArrayIndexOutOfBoundsException("下标非法,请检查");
}
// 检查顺序表是否已满
if (this.usedSize == elem.length) {
// 如果已满,则进行扩容,新的数组长度为原来的2倍
this.elem = Arrays.copyOf(elem, elem.length * 2);
}
// 将pos位置及之后的元素向后移动一位,为新元素腾出空间
for (int i = usedSize - 1; i >= pos; i--) {
elem[i + 1] = elem[i];
}
// 在pos位置插入新元素
elem[pos] = data;
// 更新顺序表中的元素个数
usedSize++;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实现逻辑
- 合法性检查:首先检查传入的下标
pos
是否在合法范围内,即是否在0到usedSize
之间。如果不在这个范围内,则抛出ArrayIndexOutOfBoundsException
异常,因为插入的位置无效。 - 空间检查和扩容:接着,检查顺序表的存储空间是否足够。如果当前存储的元素数量
usedSize
已经等于数组的长度elem.length
,说明数组已满,需要进行扩容。扩容操作通过Arrays.copyOf
完成,新数组的长度是原数组长度的2倍。 - 元素后移:为了在
pos
位置插入新元素,需要将pos
位置及之后的所有元素向后移动一位。这通过一个从后向前的循环实现,确保在移动元素时不会覆盖未处理的元素。 - 插入元素:在腾出的
pos
位置插入新元素data
。 - 更新元素计数:完成插入操作后,顺序表的元素个数
usedSize
增加1。
下面动图演示:
# 2.4 contains(包含)
contains
方法用于检查MyArrayList
顺序表中是否包含特定的值toValue
。它通过遍历顺序表中的所有元素,并与toValue
进行比较来实现。如果找到一个元素与toValue
相等,则方法返回true
,表示顺序表包含这个值;如果遍历完所有元素都没有找到相等的值,则方法返回false
,表示顺序表不包含这个值。
public boolean contains(int toValue) {
// 遍历顺序表中的所有元素
for (int i = 0; i < usedSize; i++) {
// 检查当前元素是否等于要查找的值toValue
if (elem[i] == toValue) {
// 如果找到了一个元素等于toValue,返回true
return true;
}
}
// 如果遍历完所有元素都没有找到等于toValue的元素,返回false
return false;
}
2
3
4
5
6
7
8
9
10
11
12
# 2.5 indexOf(查找)
indexOf
方法用于在MyArrayList
顺序表中查找特定值toValue
的索引。该方法遍历顺序表中的所有元素,将每个元素与toValue
进行比较。如果找到一个元素与toValue
相等,则返回该元素的索引。如果遍历完所有元素后没有找到相等的值,则返回-1
,表示toValue
不在顺序表中。
public int indexOf(int toValue) {
// 遍历顺序表中的所有元素
for (int i = 0; i < usedSize; i++) {
// 检查当前元素是否等于要查找的值toValue
if (elem[i] == toValue) {
// 如果找到了一个元素等于toValue,返回该元素的索引i
return i;
}
}
// 如果遍历完所有元素都没有找到等于toValue的元素,返回-1
return -1;
}
2
3
4
5
6
7
8
9
10
11
12
# 2.6 get(获取指定位置)
get
方法用于获取MyArrayList
顺序表中指定位置pos
的元素值。该方法首先进行下标合法性检查,确保请求的位置在顺序表的有效范围内。如果请求的位置合法,方法则返回该位置的元素值;如果位置不合法,则抛出异常,防止程序访问无效内存区域。
public int get(int pos) {
// 检查请求的位置pos是否合法
// pos的合法范围是0到usedSize-1
// 如果pos小于0或pos大于等于usedSize,说明pos超出了顺序表当前存储元素的范围
if(pos<0 || pos>=usedSize){
// 抛出异常,提示位置不合法
throw new ArrayIndexOutOfBoundsException("下标非法,请查看!");
}
// 返回顺序表中pos位置的元素
// 如果下标合法,则直接返回数组elem中下标为pos的元素
return elem[pos];
}
2
3
4
5
6
7
8
9
10
11
12
# 2.7 set(更新指定位置)
set
方法用于更新MyArrayList
顺序表中指定位置pos
的元素值为value
。与获取元素值的操作类似,更新操作之前需要进行下标合法性检查,以确保不会发生数组越界异常。如果给定的位置合法,则将该位置的元素值更新为value
。如果位置不合法,则抛出异常。
public void set(int pos, int value) {
// 检查要更新元素的位置pos是否合法
// pos的合法范围是0到usedSize-1,确保pos指向顺序表中已存在的元素
if(pos<0 || pos>=usedSize){
// 如果pos不在合法范围内,则抛出ArrayIndexOutOfBoundsException异常
// 异常消息提示"下标错误,请查看!"
throw new ArrayIndexOutOfBoundsException("下标错误,请查看!");
}
// 更新操作:将顺序表中pos位置的元素值更新为value
// 直接访问数组elem的pos索引位置,然后赋予新的值value
elem[pos] = value;
}
2
3
4
5
6
7
8
9
10
11
12
# 2.8 remove(删除指定key)
remove
方法用于从MyArrayList
顺序表中删除第一次出现的指定值key
。这个过程分为几个步骤:首先,使用indexOf
方法查找key
在顺序表中的索引;如果找到,则通过覆盖的方式移除该元素,并调整后续元素的位置;最后,更新顺序表的元素计数并将最后一个元素置为默认值(例如0)。如果未找到指定的值,则方法返回false
。
public boolean remove(int key) {
// 使用indexOf方法查找key在顺序表中的索引
int index = indexOf(key);
// 如果索引为-1,表示顺序表中没有找到指定的key值
if (index == -1) {
System.out.println("没有这个数据");
return false; // 返回false,表示删除失败
}
// 如果找到了指定的key值,开始从index位置开始,将后续元素向前移动一位
// 这个操作通过循环实现,覆盖index位置的元素,并依次向后移动
for (int i = index; i < usedSize - 1; i++) {
elem[i] = elem[i + 1];
}
// 完成元素移动后,顺序表的实际元素数量减少1
usedSize--;
// 将最后一个元素(现在是无效数据)置为0
// 这步操作是为了保持数据的整洁,避免保留已删除元素的残留值
elem[usedSize] = 0;
// 返回true,表示删除成功
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
实现逻辑
- 查找要删除的元素索引:首先调用
indexOf
方法查找key
值在顺序表中的索引。如果未找到(indexOf
返回-1
),则表示顺序表中不存在该值,直接返回false
,表示删除操作失败。 - 移动元素:如果找到了要删除的元素,则从其位置开始,将所有后续元素向前移动一位,覆盖掉要删除的元素。这通过一个循环实现,直到移动到
usedSize - 1
位置的元素。 - 更新元素数量:移动完元素后,顺序表的
usedSize
(表示顺序表中元素的数量)减1,以反映一个元素已被删除。 - 清理无效数据:为了保持数据的清洁,将现在多余的最后一个元素置为默认值(这里设置为
0
)。这一步是可选的,主要用于清理可能的残留数据。
下面动图演示:
# 2.9 size (长度)
size
方法非常直接,它返回MyArrayList
顺序表中当前存储的元素个数。这个数量直接对应于内部变量usedSize
的值。
public int size() {
// 返回顺序表当前存储的元素数量,即usedSize的值
return usedSize;
}
2
3
4
# 2.10 clear(清空)
clear
方法用于清空MyArrayList
顺序表,移除其中的所有元素。在这个简化的模型中,清空操作通过将usedSize
置为0来实现。
public void clear() {
// 将usedSize置为0,逻辑上清空顺序表
// 之后的任何操作都将基于一个空的顺序表执行
usedSize = 0;
}
2
3
4
5
# 2.11 异常的定义
ArrayIndexException
是一个运行时异常,用于表示当程序尝试访问顺序表中非法索引时的错误情况。
public class ArrayIndexException extends RuntimeException {
// 提供无参数的构造函数
public ArrayIndexException() {
super();
}
// 提供带有错误消息的构造函数
public ArrayIndexException(String message) {
super(message); // 调用超类的构造函数,传入错误消息
}
}
2
3
4
5
6
7
8
9
10
11
# 3.测试代码
public class Test {
public static void main(String[] args) {
MyArrayList MyArrayList =new MyArrayList();
//添加数据
MyArrayList.add(1);
MyArrayList.add(2);
MyArrayList.add(3);
MyArrayList.add(4);
MyArrayList.add(5);
//打印
System.out.print("当前数组元素:");
MyArrayList.display();
//值所在的下标
System.out.print("值所在的下标:");
System.out.println(MyArrayList.indexOf(3));
//是否包含这个值
System.out.print("是否包含这个值:");
System.out.println(MyArrayList.contains(2));
//获得下标所在位置元素
System.out.print("获得下标所在位置元素:");
System.out.println(MyArrayList.get(3));
//修改下标的值
System.out.print("修改下标的值:");
MyArrayList.set(3,12);
MyArrayList.display();
//删除关键字key
System.out.print("删除关键字key:");
MyArrayList.remove(2);
MyArrayList.display();
//获得长度
System.out.print("获得当前顺序表长度:");
System.out.println(MyArrayList.size());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
输出结果如下: