
在计算机科学中,线性表是最基本、最常用的一种数据结构。线性表是n个具有相同特性的数据元素的有限序列,常见的线性表包括顺序表、链表、栈和队列等。在Java集合框架中,ArrayList作为最常用的动态数组实现,基于顺序表的理念提供了丰富的操作方法。本文将深入探讨顺序表的实现原理。
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。顺序表在内存中占用连续的空间,这使得它能够通过下标直接访问元素,时间复杂度为O(1)。
顺序表的主要操作包括:
public void add(int data) | 默认在末尾增加元素 |
|---|---|
public void add(int pos, int data) | 在下标为pos的元素前增加 |
public boolean contains(int data) | 判断是否含有某个元素 |
public int indexOf(int data) | 查找某个元素的对应下标 |
public int get(int pos) | 获取下标为pos的值 |
public void set(int pos, int data) | 将下标为pos的元素设置为data |
public void remove(int data) | 删除第一次出现的data |
public int size() | 获取顺序表的长度 |
public void clear() | 清除顺序表 |
public void display() | 打印顺序表 |
顺序表的底层逻辑是数组,因此我们可以定义一个数组,大小暂定为10;为了方便统计数组的元素个数,我们定义一个整形变量usedSize用于记录顺序表已有元素的个数。
public class MyArrayList {
int[] array;
int usedSize; //默认初始化为0
public MyArrayList(int[] array) {
//使用构造方法新建一个数组,长度为10
this.array = new int[10]; //关于this关键字的用法可以去看我之前写过的类与对象那篇文章
}
}接下来我们开始实现add方法:
因此add方法的部分可以写成如下:
public void add(int data) {
if (isFull()) {
expand();
}
this.array[usedSize] = data;
this.usedSize++;
}其中 isFull方法 和 expand方法 分别是数组判满方法和数组扩容方法,具体实现如下:
public boolean isFull() {
return this.usedSize == this.array.length;
}
private void expand() {
//扩容为原来长度的两倍
this.array = Arrays.copyOf(this.array,2*this.array.length);
}第二个add其实是第一个add的重载,接下来我们来实现:
具体代码实现如下:
public add(int pos, int data) {
try {
addCheckPos(pos); //检查下标合法性
if (isFull()) { //数组判满
expand(); //扩容
}
for (int i = usedSize; i >= pos; i--) {
this.array[i] = this.array[i-1];
}
this.array[pos] = data;
usedSize++;
} catch (PositionIllegalException e) {
e.printStackTrace(); //异常处理,当异常出现时会告诉用户出现异常的代码行数
}
}其中 try...catch...语句是用于可能出现异常的代码,常用于捕捉异常并抛出。
addCheck方法 是用于检查下标合法性的方法,具体实现如下:
private void addCheckPos(int pos) {
if (pos<0 || pos>usedSize) {
throw new PositionIllegalException("Illegal position!");
}
}接着我们来看看异常的编写:
public class PositionIllegalException extends RuntimeException {
public PositionIllegalException() {
super();
}
public PositionIllegalException(String message) {
super(message);
}
}其中RuntimeException表示运行时异常,是在程序运行时出现会异常,当该异常被检测到时将会终止程序并抛出异常。
然后是 contains方法的实现:
具体实现如下:
public boolean contains(int data) {
for (int i = 0; i < this.array.length; i++) {
if (array[i] == data) {
return true;
}
}
return false;
}indexOf方法的实现其实与 contains方法的实现十分相似:
具体实现如下:
public int indexOf(int data) {
for (int i = 0; i < this.array.length; i++) {
if (this.array[i] == data) {
return i;
}
}
return -1;
}对于 get方法,我们可以:
具体实现如下:
public int public get(int pos) {
try {
isEmpty();
CheckPos(pos);
return this.array[pos];
} catch (EmptyException e) {
e.printStackTrace();
} catch (PositionIllegalException e) {
e.printStackTrace();
}
return -1;
}其中 isEmpty方法、CheckPos方法和EmptyException异常的具体实现如下:
public void isEmpty() {
if (usedSize == 0) {
throw new EpmtyException("This arraylist is empty!");
}
}
public void CheckPos(int pos) {
if (pos<0 || pos>=usedSize) {
throw new PositionIllegalException("Illegal position!");
}
}
public class EpmtyException extends RuntimeException {
public EpmtyException() {
super();
}
public EpmtyException(String message) {
super(message);
}
}set方法与 get方法大体差不多:
具体实现如下:
public void set(int pos, int data) {
try {
isEmpty();
CheckPos(pos);
this.array[pos] = data;
} catch (EmptyException e) {
e.printStackTrace();
} catch (PositionIllegalException) {
e.printStackTrace();
}
}接下来的 remove方法算是最后一个较复杂的方法了:
具体实现如下:
public void remove(int data) {
try {
isEmpty();
int pos = indexOf(data);
CheckPos(pos);
for (int i = pos; i < usedSize-1; i++) {
this.array[i] = this.array[i+1];
}
usedSize--;
} catch (EmptyException e) {
e.printStackTrace();
} catch(PosIllegalException e) {
e.printStackTrace();
}
}接下来的 size方法比较简单:我们只需要返回 usedSize的大小即可
具体实现如下:
public int size() {
return this.usedSize;
}clear方法只需要将usedSize置0即可:
public void clear() {
this.usedSize = 0;
}display方法只需要遍历顺序表然后逐个打印即可:
public void display() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.array[i] + " ");
}
System.out.println();
}至此,顺序表的基本功能我们就全都模拟实现了。
完