Java数据结构--List---顺序表

1. List的概念

List是一个接口不能直接实例化,如果要使用,必须实例化List的实现类继承自CollectionCollection也是一个接口。

List线性表n个具有相同类型元素有限序列,在该序列上可以增删查改等操作。

2. 线性表

线性表是n个具有相同特性的数据元素有限序列。常见的线性表:顺序表,链表,栈,队列

线性表在逻辑上线性结构,但在物理结构上不一定是连续的,线性表在物理上存储时,通常以数组链式结构形式存储。

顺序表:
在这里插入图片描述
链表:

在这里插入图片描述

3. 顺序表

顺序表:一段物理地址连续存储单元依次存储数据元素的线性结构,一般用数组存储,在数组上完成增删查改。

3.1 顺序表的分类:

❤静态顺序表:使用定长数组存储元素
❤动态顺序表:使用动态开辟的数组存储元素

4. ArrayList

1.ArrayList是一个普通的类实现了List接口

2.ArrayList底层使用连续的空间,底层通过数组来实现,任意位置插入或删除元素时,要将该位置后序元素整体往后或者整体往前移,时间复杂度为O(n)

3.扩容要申请新的空间,拷贝数据,释放旧空间,会有不小的消耗

4.扩容一般为2倍的增长,会有一定空间的浪费。比如当前容量100,满了以后扩容到200,再往里填入5个元素,那么会浪费95个空间

注意:

1.ArrayList是以泛型方式实现的,使用时必须要实例化

2.ArrayList底层是一段连续的空间可以动态扩容,是一个动态类型顺序表

4.1 ArrayList的手动实现

4.1.1 顺序表基本功能

public class MyArrayList {
    //静态顺序表
    private int[] elem;
    private int usedsize;
    private static final int DEFAULT_SIZE=4;
    //初始化顺序表
    public MyArrayList(){
        this.elem=new int[DEFAULT_SIZE];
    }
    //对顺序表进行扩容
    public void expand(){}
    //判断顺序表是否为空
    public boolean ifEmpty(){}
    //判断顺序表是否满了
    public boolean ifFull(){}
    //打印顺序表
    public void display(){}
    //新增元素,默认在顺序表最后增加
    public void add(int val){}
    //新增元素,在顺序表头部增加
    public void addHead(int val){}
    //在pos位置新增元素
    public void addPos(int pos,int val){}
    //删除头元素
    public void removeHead(){}
    //删除尾元素
    public void removeTail(){}
    //删除pos位置的元素
    public void removePos(int pos){}
    //删除第一次出现的关键字key
    public void remove(int toremove){}
    //判断是否包含某个元素
    public boolean contains(int val){}
    //查找某个元素对应的pos
    public int indexOf(int val){}
    //获取pos位置的元素
    public int getPosVal(int pos){}
    //给pos位置的元素设置val
    public void setPosVal(int pos){}
    //获取顺序表长度
    public int size(){}
    //清空顺序表
    public void clear(){}
}

4.1.2 基本功能的具体实现

❤对顺序表的扩容

public void expand(){
        this.elem= Arrays.copyOf(this.elem,this.usedsize*2);
        System.out.println("已经扩容");
    }

❤判断顺序表是否为空

public boolean ifEmpty(){
        if(this.usedsize==0){
            return true;
        }
        return false;
    }

❤判断顺序表是否满了

public boolean ifFull(){
        if(this.elem.length==this.usedsize){
            return true;
        }
        return false;
    }

❤打印顺序表

//方法一:
public void display(){
        for(int i=0;i<this.usedsize;i++){
            System.out.print(elem[i]);
        }
        System.out.println();
    }
//方法二:
System.out.println(Arrays.toString(this.elem));

❤获取顺序表有效长度

public int size(){
        return this.usedsize;
    }

❤清空顺序表

public void clear(){
        for(int i=0;i<this.usedsize;i++){
            elem[i]=0;
        }
        this.usedsize=0;//这是关键的一步
    }

4.1.3 增删查改四大功能

❤头插

private void addHead(int data){
        if(ifFull()){
            System.out.println("满了,需要扩容");
            expand();
        }
        for(int i=this.usedSize;i>0;i--){//从后往前挪动数据,把表头位置腾出来
            elem[i]=elem[i-1];
        }
        this.elem[0]=data;
        this.usedSize++;
    }

❤尾插

private void addTail(int data){
        if(ifFull()){
            System.out.println("满了,需要扩容");
            expand();
        }
        this.elem[this.usedSize]=data;//这两行也可以写成this.elem[this.usedSize++]=data;
        this.usedSize++;
    }

❤指定下标插入

private void addPos(int pos,int data){
        //先判断下标是否合法
        if(pos<0||pos>this.usedSize){
            System.out.println("下标不合法,插入失败");
            return;
        }
        //判断是否满了
        if(ifFull()){
            System.out.println("满了,需要扩容");
            expand();
        }
        else{
            for(int i=this.usedSize-1;i>=pos;i--){//从后向前挪动,不会造成元素覆盖
                this.elem[i+1]=this.elem[i];
            }
            this.elem[pos]=data;
            this.usedSize++;
        }
    }

❤头删

private void removeHead(){
        //先判断是否为空,为空的话是不能删除的
        if (ifEmpty()){
            System.out.println("数组为空,不能删除");
            return;
        }
        for(int i=0;i<this.usedSize-1;i++){
            this.elem[i]=this.elem[i+1];
        }
        this.elem[this.usedSize-1]=0;//现在的最后一个元素是原来的倒数第二个元素,所以原来的最后一个元素要置为0
        this.usedSize--;
    }

❤尾删

private void removeTail(){
        if (ifEmpty()){
            System.out.println("数组为空,不能删除");
            return;
        }
        this.elem[this.usedSize-1]=0;
        this.usedSize--;
    }

❤指定下标元素的删除

private void removePos(int pos){
        if (ifEmpty()){
            System.out.println("数组为空,不能删除");
            return;
        }
        if(pos<0||pos>=this.usedSize){
            System.out.println("下标不合法,删除失败");
            return;
        }
        for (int i=pos;i<this.usedSize-1;i++){
            this.elem[i]=this.elem[i+1];
        }
        this.elem[this.usedSize-1]=0;
        this.usedSize--;
    }

❤删除首次出现的指定元素

private void removeKey(int toRemove){
        if (ifEmpty()){
            System.out.println("数组为空,不能删除");
            return;
        }
        //遍历数组
        for (int i=0;i<this.usedSize;i++){
            if(elem[i]==toRemove){
                //挪动元素
                for(int j=i;j<this.usedSize-1;j++){
                    this.elem[j]=this.elem[j+1];
                }
                this.elem[this.usedSize-1]=0;
                this.usedSize--;
                return;//因为只删除第一次出现的
            }
        }
    }

❤获取指定位置的元素

private int getPos(int pos){
        if(pos<0||pos>=this.usedSize){
            System.out.println("下标不合法,获取失败");
            //这里抛出异常,下面的代码就不会执行了
        }
        return this.elem[pos];
    }

❤获取指定元素所在的位置

private int indexOf(int data){
        for (int i=0;i<this.usedSize;i++){
            if(this.elem[i]==data){
                return i;
            }
        }
        System.out.println("没有找到");
        return -1;
    }

❤查找表中是否包含某个元素

private boolean contains(int data){
        for (int i=0;i<this.usedSize;i++){
            if(this.elem[i]==data){
                return true;
            }
        }
        return false;
    }

❤将pos位置的元素设置为value

private void setPos(int pos,int value){
        if(pos<0||pos>this.usedSize){
            System.out.println("下标不合法,失败");
            return;
        }
        //特殊情况:如果pos==usedSize,相当于增加新元素
        if (pos==this.usedSize){
            this.elem[pos]=value;
            this.usedSize++;
        }else {
            this.elem[pos]=value;
        }
    }

4.1.4 ArrayList的构造

例:

public static void main(String[] args) {
        //创建一个空的列表
        List<Integer> list1=new ArrayList<>();
        //创建一个有10个容量的列表
        List<Integer> list2=new ArrayList<>(10);
        list2.add(1);
        list2.add(2);
        list2.add(3);
        //list2.add("zuozuo");//编译失败,List<Integer>限定了list2中只能存放整形元素
        ArrayList<Integer> list3=new ArrayList<>(list2);//list3构造好之后,与list2中的元素一样
        for (int x:list3) {
            System.out.print(x+" ");
        }
        //避免省略类型,否则任意类型的数据都可以存放,使用时不方便
        List list4=new ArrayList();
        list4.add("zuozuo");
        list4.add(9);
    }

4.1.2 ArrayList的遍历

有三种方式:for循环,foreach,迭代器

例:

public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //for循环遍历
        for(int i=0;i<list.size();i++){
            System.out.print(list.get(i)+" ");
        }
        System.out.println();
        //foreach遍历
        for(Integer x:list){
            System.out.print(x+" ");
        }
        System.out.println();
        //迭代器遍历
        Iterator<Integer> it=list.listIterator();
        while(it.hasNext()){
            System.out.print(it.next()+" ");
        }
        System.out.println();
    }

4.2.3 ArrayList的常见操作

例:

public static void main(String[] args) {
        //创建一个存储String类型的列表
        List<String> list=new ArrayList<>();
        //向里面添加数据
        list.add("zuozuo");
        list.add("miaomiao");
        list.add("huahua");
        list.add("lanlan");
        list.add("weiwei");
        //获取list中的有效元素个数
        System.out.println(list.size());
        //获取指定下标的元素,这个下标必须介于[0,size)之间
        System.out.println(list.get(1));
        //给指定下标设置值,
        list.set(0,"mimi");
        //在指定下标插入元素,该下标及之后的元素都要统一向后移一个位置
        list.add(0,"doudou");
        System.out.println(list);
        //删除指定元素,找到了就删除,该元素之后的其他元素统一往前移动一个位置
        list.remove("doudou");
        //删除指定下标的元素
        list.remove(0);
        boolean b=list.contains("huahua");//这个方法返回boolean类型的返回值,如果找到返回true,没有找到返回false
        //查照元素在列表中第一次出现的位置:indexOf从前往后查,lastIndexOF从后往前查
        System.out.println(list.indexOf("huahua"));
        //将list中[0,4)之间的元素构成一个新的List返回
        List<String> ret=list.subList(0,4);
        System.out.println(ret);
        //清空列表
        list.clear();
    }

例题(杨辉三角)

杨辉三角

public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> ret = new ArrayList<List<Integer>>();
        for (int i = 0; i < numRows; ++i) {
            List<Integer> row = new ArrayList<Integer>();
            for (int j = 0; j <= i; ++j) {
                if (j == 0 || j == i) {
                    row.add(1);
                } else {
                    row.add(ret.get(i - 1).get(j - 1) + ret.get(i - 1).get(j));
                }
            }
            ret.add(row);
        }
        return ret;
    }

注意:

1.利用List接口创建动态数组

2.先利用外层循环创建外层动态数组,将内层动态数组的创建放在外层循环第一行,保证每一次外层循环都会初始化一个内层数组

3.内层循环用来往内层数组中添加元素,杨辉三角中第一列全为1,当i==j时值也为1,其余情况[i][j]=[i-1][j]+[i-1][j-1]

4.内层循环结束后,说明内层数组创建完成,然后就要将这个创建好的内层数组放到外层数组中

5.当外层循环结束后,说明整个二维动态数组创建完成,返回这个二维动态数组

4.2.5 补充

例:

public class Poker {
    //Test7
    public int rank;
    public String suit;

    public Poker(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public String toString() {
        return "Poker{" +
                "rank=" + rank +
                ", suit='" + suit + '\'' +
                '}';
    }
}
public class Test7 {
    public static void main(String[] args) {
        List<Poker> pokers1=new ArrayList<>();
        ArrayList<Poker> pokers2=new ArrayList<>();
    }
}

问题:上述pokers1和pokers2的区别是什么?

pokers1发生了向上转型,pokers1只能调用List里面自己的方法ArrayList里面的方法不能调用。pokers2则是当前类的方法都能调用。

用接口来接收对象,好处是:可以发生向上转型,可以接收不同的对象,只要实现了这个接口的都可以

4.3 ArrayList的优缺点

优点:给定下标的时候,查找速度非常快。适合给定下标的查找,时间复杂度O(1)

缺点:插入删除必须要挪动元素,扩容浪费一定的空间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值