在集合框架中,List是一个接口,继承自Collection。
Collection也是一个接口,该接口中规范了后序容器中常用的一些方法,具体如下所示:
Iterable也是一个接口,表示实现该接口的类是可以逐个元素进行遍历的,具体如下:
List 的官方文档
站在数据结构的角度来看,List就是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以执行增删改查以及变量等操作。
List中提供了好的方法,具体如下:
虽然方法比较多,但是常用方法如下:
注意:List是个接口,并不能直接用来实例化
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
在数组上完成数据的增删查改。
我们现在有这样一个SepList接口为:
public interface SepList {
// 新增元素,默认在数组最后新增
void add(int data);
// 在 pos 位置新增元素
void add(int pos, int data);
// 判定是否包含某个元素
boolean contains(int toFind);
// 查找某个元素对应的位置
int indexOf(int toFind);
// 获取 pos 位置的元素
int get(int pos);
// 给 pos 位置的元素设为 value
void set(int pos, int value);
//删除第一次出现的关键字key
void remove(int key);
// 获取顺序表长度
int size();
// 清空顺序表
void clear();
}
这里博主将在一个MyArrayList类里面实现这些接口
public class MyArrayList implements SepList {
private int[] elem;//数组
private int usedSize;//记录有效的数据的个数
private static final int DEFAULT_SIZE = 10;//最初的数据容量
public MyArrayList() {
this.elem = new int[DEFAULT_SIZE];
}
// 新增元素,默认在数组最后新增
public void add(int data) { }
// 在 pos 位置新增元素
public void add(int pos, int data) { }
// 判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 查找某个元素对应的位置
public int indexOf(int toFind) { return -1; }
// 获取 pos 位置的元素
public int get(int pos) { return -1; }
// 给 pos 位置的元素设为 value
public void set(int pos, int value) { }
//删除第一次出现的关键字key
public void remove(int key) { }
// 获取顺序表长度
public int size() { return 0; }
// 清空顺序表
public void clear() { }
}
在增加一个元素前,我们需要对该顺序表进行判断,判断是否已满,若满则需要进行扩容
每增加一个元素,我们我们记录有效个数的usedSize加1
// 新增元素,默认在数组最后新增
public void add(int data) {
//判断数组是否已经被装满
if(usedSize >= this.elem.length) {
//被装满后需要进行扩容
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
this.elem[this.usedSize] = data;
usedSize++;
}
在增加一个元素前,我们需要对该顺序表进行判断,判断是否已满,若满则需要进行扩容
我们还需要多pos进行一个判断,判断它是否合法,如果不合法,我们抛出异常进行提醒
在pos位置增加元素,需要将pos位置及以后的元素进行后移一位,然后再在pos位置新增元素
每增加一个元素,我们我们记录有效个数的usedSize加1
//自定义异常
class PosWrongfulException extends RuntimeException {
public PosWrongfulException(String message) {
super(message);
}
}
// 在 pos 位置新增元素
public void add(int pos, int data) throws PosWrongfulException {
//判断数组是否已经被转满
if(usedSize >= this.elem.length) {
//被装满后需要进行扩容
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
if(pos < 0 || pos > this.usedSize) {
System.out.println("pos位置不合法!");
throw new PosWrongfulException("pos位置不合法");
}
for(int i = this.usedSize;i >= pos;i--) {
//pos位置以及pos位置以后的数据整体进行后移
this.elem[i] = this.elem[i-1];
}
this.elem[pos] = data;
usedSize++;
}
遍历即可
// 判定是否包含某个元素
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;
}
获取前我们得进行判断,该顺序表类元素不能为空
我们还得对pos进行是否合法得判断
//自定义的异常
class PosWrongfulException extends RuntimeException {
public PosWrongfulException(String message) {
super(message);
}
}
class EmptyException extends RuntimeException {
public EmptyException(String message) {
super(message);
}
}
// 获取 pos 位置的元素
public int get(int pos)throws PosWrongfulException {
if(this.usedSize == 0)
{
throw new EmptyException("当前顺序表为空!");
}
if(pos < 0 || pos > this.usedSize) {
System.out.println("pos位置不合法!");
throw new PosWrongfulException("pos位置不合法");
}
return this.elem[pos];
}
我们依旧需要判断pos的合法性,前面已经自定义了该异常,这里就不再进行定义了
然后将pos位置的元素改为value就好
// 给 pos 位置的元素设为 value
public void set(int pos, int value) {
if(pos < 0 || pos > this.usedSize) {
System.out.println("pos位置不合法!");
throw new PosWrongfulException("pos位置不合法");
}
this.elem[pos] = value;
}
我们依旧需要判断数组是否为空
遍历数组,若没有我们要删除的元素,我们便进行提示后退出
若有,则只需要用后面的数据对前面进行覆盖就好
//删除第一次出现的关键字key
public void remove(int key) {
if(this.usedSize == 0) {
throw new EmptyException("顺序表为空!");
}
int index = this.indexOf(key);
if(index == -1) {
System.out.println("没有这个数字");
return;
}
//进行覆盖
for (int i = index; i < size()-1; i++) {
this.elem[i] = this.elem[i+1];
}
//如果不是基本类型,将usedSize下标置为空
//this.elem[this.usedSize] = null;
this.usedSize--;
}
这个就非常简单了,只需要返回usedSize就好
// 获取顺序表长度
public int size() {
return this.usedSize;
}
对于当前基本类型的数据来说,只需要将usedSize置为0就好
public void clear() {
this.usedSize=0;
}
线性表的顺序存储结构,在存、读数据时,不管是哪个位置,时间复杂度都是O(1);而插入或删除时,时间复杂度都是O(n)。这就说明,它比较适合元素个数不太变化,而更多是存取数据的应用。当然,它的优缺点还不只这些……
无需为表示表中元素之间的逻辑关系而增加额外的存储空间可以快速地存取表中任一位置的元素
插入删除操作需要移动大量元素,当线性表长度变化较大时,难以确定存储空间的容量,造成存储空间的碎片
关于《【数据结构】 List与顺序表及接口的实现》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!