[数据结构][Java]顺序表的使用和模拟实现(超详细!)

一、模拟实现

顺序表的底层结构是数组,我们通过自定义类MyArrayList模拟其核心功能,支持元素的增、删、改、查等操作:

  1. 顺序表结构需要的三个基本属性
public class MyArrayList {
	private int[] elem; //数组,存储数据元素,这里以存储int类型为例
	private int usedSize; //顺序表当前长度
	private final int DEFAULT_SIZE = 10; //默认容量
}
  1. 提供构造方法
//不带参数构造方法
public MyArrayList() {
	this.elem = new int[DEFAULT_SIZE];
}

//带参数构造方法(指定初始容量)
public MyArrayList(int capacity) {
	this.elem = new int[capacity];
}

其中,usedSize默认为零,无需在构造方法中设置值

  1. 遍历打印
public void display() {
	for(int i = 0; i < this.usedSize; i++) { //遍历数组
		System.out.print(this.elem[i] + " ");
	}
	System.out.println(); //换行,美化输出
}
  1. 判断当前数组是否已满
public boolean isFull() {
	return this.usedSize == this.elem.length;
}

当usedSize等于数组长度是,说明数组已满,需要扩容

  1. 新增元素,默认在最后新增
import java.util.Arrays;
public void add(int data) {
	//数组已满需要扩容,以扩容到原长度2倍为例
	if(isFull()) {
		this.elem = Arrays.copyOf(this.elem, this.elem.length * 2);
	}
	this.elem[usedSize] = data;
	this.usedSize++; //元素个数+1
}
  1. 指定位置新增元素,第一个元素下标为0
public void add(int pos, int data) {
	//判断位置是否合法
	if(pos < 0 || pos > usedSize) {
		System.out.println("插入位置不合法!"); //此处也可抛出异常
	}
	
	//数组已满需要扩容,以扩容到原长度2倍为例
	if(isFull()) {
		this.elem = Arrays.copyOf(this.elem, this.elem.length * 2);
	}

	//从插入位置开始,每个元素需要向后移一位(空出位置插入新元素)
	//从后向前移动,避免元素被覆盖
	for(int i = this.usedSize - 1; i >= pos; i--) { 
		this.elem[i + 1] = this.elem[i];
	}

	this.elem[pos] = data; //在pos位置放入新元素
	this.usedSize++; //元素个数+1
}

在这里插入图片描述

  1. 判断是否包含某个元素
public boolean contains(int toFind) {
	//遍历数组查找
	for(int i = 0; i < this.usedSize; i++) {
		if(this.elem[i] == toFind) {
			return true;
		}
	}
	return false;
}
  1. 查找某个元素位置,返回元素第一次出现的位置
public int indexOf(int toFind) {
	//遍历数组查找
	for(int i = 0; i < this.usedSize; i++) {
		if(this.elem[i] == toFind) {
			return i;
		}
	}
	return -1; //找不到返回-1
}
  1. 获取pos位置元素
public int get(int pos) {
	//判断位置是否合法
	if(pos < 0 || pos >= usedSize) {
		System.out.println("位置不合法!"); //此处也可抛出异常
	}
	return this.elem[pos];
}
  1. pos位置元素更新为value
public void set(int pos, int value) {
	//判断位置是否合法
	if(pos < 0 || pos >= usedSize) {
		System.out.println("位置不合法!"); //此处也可抛出异常
	}
	this.elem[pos] = value;
}
  1. 删除第一次出现的关键字key
public void remove(int key) {
	//找到元素位置
	int index = indexOf(key);

	//处理元素不存在情况
	if(index == -1) {
		System.out.println("没有这个元素!"); //此处也可抛出异常
		return;
	}
	
	//删除元素:让后面的元素一次向前移一位,覆盖掉要删除的元素
	for(int i = index + 1; i < usedSize; i++) {
		this.elem[i - 1] = this.elem[i];
	}

	//元素个数-1
	this.usedSize--;
}
  1. 删除所有关键字key
public void removeAll(int key) {
	int count = 0; //记录非key元素个数
	//遍历数组,将非key元素放到count位置
	for(int i = 0; i < this.usedSize; i++) {
		if(this.elem[i] != key) {
			elem[count++] = elem[i];
		}
	}
	this.usedSize = count; //修改元素个数
}
  1. 获取顺序表长度
public int size() {
	return this.usedSize;
}
  1. 清空顺序表
public void clear() {
	this.usedSize = 0;
	//若存储累类型为引用类型(如String, Object),需额外将元素置为null
	//for(int i = 0; i < this.usedSize; i++) {
	//		this.elem[i] = null;
	//}
	//this.usedSize = 0;
}

[注意]
对于基本类型(int、double 等),直接置usedSize=0即可;
对于引用类型,需先将元素置为null(释放对象引用),再重置usedSize,避免内存泄漏。

二、用法

1. 常用构造方法

构造方法 说明
ArrayList() 无参构造,初始容量10
ArrayList(Collection<? extends E> c) 利用其他集合构建ArrayList
ArrayList(int capacity) 制定初始容量

2. 顺序表中一些常用方法

方法 功能
boolean add(E e) 尾部新增元素
void add(int index, E element) 指定位置新增元素
boolean contains(Object o) 判断是否包含元素
E get 获取指定位置元素
int indexOf(Object o) 查找元素第一次出现的位置(未找到返回 - 1)
E remove(int index) 删除指定位置元素(返回被删除元素)
boolean remove(Object o) 删除第一次出现的元素
E set(int index, E element) 修改指定位置元素,返回旧元素
int size() 返回元素个数
void clear() 清空顺序表
boolean isEmpty 判断是否为空

3. 遍历方法

  1. for 循环
  2. for each 循环(无法获取下标,适合只遍历不修改)
for(Integer num : list) {
	System.out.print(num + " ");
}
  1. 迭代器

以Integer类型为例:

import java.util.Iterator();
Iterator < Integer > it = list.iterator();
while(it.hasNext()) {
	System.out.print(it.next() + " ");
}

三、总结

顺序表是线性表的一种实现方式,底层通过数组存储数据,元素在内存中连续存放。通过对顺序表中方法的模拟实现,我们可以清楚地知道顺序表有如下特点:

  1. 元素按属性排列,可通过下标直接访问
  2. 插入/删除元素师需要移动大量元素,效率较低
  3. 容量固定,可动态扩容

因此,顺序表适合一些查询操作频繁,插入/删除操作较少的场景。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐