目录
1. 引言
顺序表(ArrayList)的概念及其在编程中的重要性。
为什么选择自己实现而不是直接使用Java库中的ArrayList。
2. 基本原理
3. 类定义与属性
MyArrayList类的定义
4. 核心方法
5. 错误处理与边界情况
6. 结论
顺序表(ArrayList)是一种动态数组,能够根据需要自动调整大小,支持高效的随机访问和顺序存储。它在编程中非常重要,因为提供了灵活性以适应数据量的变化、高效的元素访问能力,并且拥有丰富的API支持,便于操作和集成。这使得顺序表成为处理动态数据集、实现算法及构建复杂应用的理想选择,广泛应用于数据缓存、动态列表管理等多种场景。简而言之,顺序表是现代编程中不可或缺的基础数据结构之一。.
自己实现顺序表而不是直接使用Java库中的ArrayList,主要是为了深入理解其内部工作原理,如动态扩展和元素操作机制,同时也能根据特定需求进行定制优化,增强对数据结构的掌握,并提升编程技能。此外,自定义实现还能帮助你在学习过程中发现并解决潜在问题,积累宝贵的编程经验。简而言之,自实现有助于加深理解、灵活应用和技能提升。
顺序表(如Java中的ArrayList)基于动态数组的基本原理工作。它通过一个内部数组来存储元素,并根据需要自动调整数组的大小。初始时,顺序表分配一个固定容量的数组,当添加新元素导致数组满载时,顺序表会创建一个新的、容量更大的数组(通常为原数组的两倍),并将原有元素复制到新数组中,从而实现容量扩展。这种机制确保了顺序表可以在不预先知道数据量的情况下高效地管理数据,同时保持随机访问的高效性(时间复杂度为O(1))。简而言之,顺序表通过动态数组和容量扩展机制,提供了灵活且高效的元素存储与访问能力。
//实现 ArrayList 类
public class MyArrayList {
public int[] elem;//用于存储列表元素的数组
public int usedSize;//记录当前已使用的元素数量。
//默认容量
private static final int DEFAULT_SIZE = 10;
}
int[] elem;:用于存储列表元素的数组。
int usedSize;:记录当前已使用的元素数量。
private static final int DEFAULT_SIZE = 10;:默认容量大小。
构造函数:初始化对象时的行为
public MyArrayList() {//构造函数
this.elem = new int[DEFAULT_SIZE];
}
display():打印当前顺序表的方法。
public void display() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i] + " ");
}
}
add():添加元素到顺序表末尾或指定位置的方法,包含扩容逻辑。
public void add(int data) {
if (isFull()) {
this.elem = Arrays.copyOf(this.elem, 2*this.elem.length);
}
this.elem[this.usedSize] = data;
this.usedSize++;
}
public void add(int pos, int data) {
checkPosInAdd(pos);
if (isFull()) {
this.elem = Arrays.copyOf(this.elem, 2*this.elem.length );
}
for (int i = this.usedSize - 1; i >= pos; i--) {
this.elem[i + 1] = this.elem[i];
}
this.elem[pos] = data;
this.usedSize++;
}
isFull():判断顺序表是否已满的方法。
public boolean isFull() {
if (this.usedSize == this.elem.length) {
return true;
}
return false;
}
contains():检查顺序表中是否包含某个元素。
public boolean contains(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if (this.elem[i] == toFind) {
return true;
}
}
return false;
}
indexOf():查找元素在顺序表中的索引位置。
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if (this.elem[i] == toFind) {
return i;
}
}
return -1;
}
get():获取指定位置的元素。
public int get(int pos) {
checkPosInget(pos);
return this.elem[pos];
}
set():更新指定位置的元素值。
public void set(int pos, int value) {
checkPosInget(pos);
this.elem[pos] = value;
}
remove():移除第一次出现的指定元素。
public void remove(int key) {
int x = indexOf(key);//如果有,x为key的下标;
if (x == -1) {
System.out.println("no number");
}
//int y = this.elem[key];
for (int i = x; i < this.usedSize-1; i++) {
this.elem[i] = this.elem[i+1];
}
this.usedSize--;
}
size():返回顺序表中元素的数量。
public int size() {
return this.usedSize;
}
clear():清空顺序表的所有元素。
public void clear() {
this.usedSize = 0;
}
超出边界访问:当尝试访问或操作超出有效范围的索引时,应进行边界检查并给出适当的提示或处理。
private boolean checkPosInAdd(int pos) {
if (pos < 0 || pos > this.usedSize) {
return false;
}
return true;//合法
}
private boolean checkPosInget(int pos) {
if (pos < 0 || pos >= this.usedSize) {
return false;
}
return true;//合法
}
删除不存在的元素
public void remove(int key) {
int x = indexOf(key);//如果有,x为key的下标;
if (x == -1) {//处理不存在时的情况
System.out.println("no number");
}
//int y = this.elem[key];
for (int i = x; i < this.usedSize-1; i++) {
this.elem[i] = this.elem[i+1];
}
this.usedSize--;
}
添加元素时数组已满
public boolean isFull() {
if (this.usedSize == this.elem.length) {
return true;
}
return false;
}
在实现顺序表的过程中,我首先定义了MyArrayList类及其关键成员变量,包括存储数据的数组elem、记录已使用元素数量的usedSize和默认容量DEFAULT_SIZE,并通过构造函数进行了初始化。接着,我实现了核心方法,如添加、删除、获取和设置元素,并在这些方法中加入了边界检查和容量扩展逻辑,以处理各种异常情况,比如超出边界访问和删除不存在的元素。通过这个过程,我不仅深入理解了动态数组的工作原理和内部机制,还提升了对数据结构的掌握和编程技能,学会了如何根据具体需求定制和优化数据结构。这次实践让我更加熟悉了代码健壮性的构建方式,也增强了我在实际开发中解决问题的能力。总的来说,自定义实现顺序表是一次非常有价值的学习经历。