java数据结构-顺序表之役(源代码)

在说到顺序表时,最开始要提到的必然是线性表,下面先说说线性表

线性表

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

线性表在逻辑上是线性结构,但在物理结构上却不一定连续,也就是说在物理上存储时,线性表会以数组和链式结构存储

例如图上所示

关于线性表,我们会有以下的思考和总结

顺序表

简单说完线性表后,我们来具体说说我们本次的主角顺序表。

即是一段物理地址连续的存储单元一次存储数据元素的线性结构,一般情况下采用数组存储。

在数组上完成数据的增删查改

//新增元素,默认在数组最后新增
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 toRemove){}
//获取顺序表的长度
public int size(){return 0;}
//清空顺序表
public void clear(){}
//打印顺序表
public void display(){}

今天,我们的主要任务便是,把这一座座高地的底层代码深挖出来。

public class MyArrayList {

    private int[] elem;//存放数据元素
    private int usedSize;//usedSize代表当前顺序表当中有效数据个数
    private static final int DEFAULT_SIZE = 2;

    public MyArrayList() {
        this.elem = new int[DEFAULT_SIZE];
    }

    //指定容量 initCaoacity
    public MyArrayList(int initCaoacity) {
        this.elem = new int[initCaoacity];
    }

这些是我们前面的定义

首先我们先分析display()

public void display()

public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.println(this.elem[i] + "");
        }
    }

display函数的实质就是遍历数组

public void 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 boolean isFull(){
    if(this.usedSize == this.elem.length){
        return true;
    }
        return false;
    }

数据结构是一门逻辑非常严谨的学科,不能直接就新增元素,假如只到下标4我们的空间就满了,那么此时我们要考虑越界,考虑扩容。当然,在我们考虑是否扩容是,我们要关注常量DEFAULT_SIZE定义的值,此时当我们在main方法中实现一个1,2,3,4,5时,我们可以发现,会扩容两次,具体可以自己调试试一下。(最好是自己来debug一下,加深理解)

public void add(int pos, int data)

这个方法与上面add,构成了方法的重载。但与上面的add()有不同,这个add是从从中插入一个数据,我们思考到底层代码的逻辑就是把数据逐步从最后面挪开

可以给大家画一个图加深理解一下。

把4挪到5,3挪到4,2挪到3,我们一定是从后往前挪,我们的核心思想就是elem[i+1]=elem[i]

public void add(int pos, int data) {
        //pos合法性判断
        if(pos<0 || pos>this.usedSize){
            throw new PosOutOfBoundsException("位置不合法");
            //System.out.println("位置不合法");
            //return;
        }
        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++;
    }

异常的代码: 

//位置超过越界异常   运行异常
public class PosOutOfBoundsException extends RuntimeException {
    public PosOutOfBoundsException() {
    }

    public PosOutOfBoundsException(String message) {
        super(message);
    }
}

测试截图如下

public boolean contains(int toFind){return true}

判断是否包含某个元素

public boolean contains(int toFind) {
        for (int i=0;i<this.usedSize;i++) {
            if (this.elem[i] == toFind){   //==或equals或compareTo
                return true;
            }
        } 
        return  true;
    }

简单的想法就是进行一遍遍历,但如果当我们的要找的是一个引用类型,此时我们就不能用等号来

常用的我们需要用到equals和compareTo两个,但compareTo返回的类型是整形,比较大于的话返回1,小于返回-1,等于返回0。而equals的返回值是true和false,因此我们要使用哪种方法就要具体环境具体来应用。

public int indexOf(int toFind){return -1}

查找某个元素对应位置

此方法与Boolean contains方法高度重合,我们要做的只需要改变返回值

 public int indexOf(int toFind) {
        for (int i=0;i<this.usedSize;i++) {
            if (this.elem[i] == toFind){
                return i;
            }
        }

        return -1;
    }

两个方法测试 ↓ 

public int get(int pos){return -1}

获取pos位置的元素

public int get(int pos) {
        //判断pos的合法性
        if(pos<0 || pos >= this.usedSize) {
            //位置不合法就抛出异常
            throw new PosOutOfBoundsException("位置不合法");
        }
        return this.elem[pos];
    }

异常的代码上面有,就不多说了 


public void set(int pos,int value){}

给pos位置的元素设为value 

大家可能会认为set会与add比较相像

但这个设置我们可以理解为更新,不一定来移动元素,直需要pos在合法范围内,我们便可以直接把pos覆盖掉

同样的,我们要先检查pos的合法性,但总是需要写两行代码来检查pos的合法性,我们觉得代码太冗杂了,所以我们采用一个封装的思想,把这两行代码写成一个checkpos方法。↓

//给pos位置的元素设为value 或者叫更新
    public void set(int pos, int value) {
        checkPos(pos);
        this.elem[pos] = value;
    }
    public void checkPos(int pos) {
        if (pos < 0 || pos >= this.usedSize) {
            //位置不合法就抛出异常
            throw new PosOutOfBoundsException("位置不合法");
        }
    }

public void remove(int toRemove){}

删除第一次出现的关键词key 

//删除第一次出现的关键字Key
    public void remove(int toRemove) {
        int index = indexOf(toRemove);
        if(index == -1){
            System.out.println("没有这个数据");
        return;
        }
        for (int i=index;i<this.usedSize-1;i++){
            this.elem[i] = this.elem[i+1];
        }
        //最后因为是从后往前移,少一个元素,我们用--来更新usedsized
        this.usedSize--;
    }

可以实现删去头个元素,也可以删尾巴,同时不存在的东西删掉也有显示。

public int size(){return 0;}

获取顺序表的长度

 //获取顺序表的长度
    public int size() {
        return this.usedSize;
    }

public void clear(){}

清空数据表

最简单的方法全部置为0,当我们面对的基本数据类型是没有区别,也完全没有问题。

当我们数组中存储的是一个个地址时,比如

我们就必须要牵扯一下内存泄露这个问题

也就是说0(elem[0])下标的地址上一定引用了一个对象,当我们用usedsized=0时,这种置零方法不可取, 我们clear要清空列表,我们只是把usedsized=0,置为0认为0下标没有可用的内存了,那么理论上来说0x88就不该再去占用我的内存,同样我们也可用说ox87,ox86这些就一直无法释放内存了

简单来说就是地址的引用,内存一直不被释放。

以[i]=[i+1]为例,当我要删除,我们要从后往前覆盖,我们就要知道[i+1]会一直被引用,一直不被释放,我们面对引用数据类型时应该怎么办呢?

最复杂,最笨的方法就是把它们一个一个置为null

public void clear() {
        //面对引用数据类型
        /*for (int i=0;i<this.usedSize;i++){
            this.elem[i]=null;
        }*/
        this.usedSize=0;
    }

目前,我们就自己实现了一个顺序表,写完了顺序表的整体的底层代码,在以后我们要用到这些方法时,只需要直接引用就好了。

整体的源代码就如下

MyArrayList.java

import java.lang.reflect.Array;
import java.util.Arrays;

public class MyArrayList {

    private int[] elem;//存放数据元素
    private int usedSize;//usedSize代表当前顺序表当中有效数据个数
    private static final int DEFAULT_SIZE = 2;

    public MyArrayList() {
        this.elem = new int[DEFAULT_SIZE];
    }

    //指定容量 initCaoacity
    public MyArrayList(int initCaoacity) {
        this.elem = new int[initCaoacity];
    }



    //新增元素,默认在数组最后新增
    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 boolean isFull(){
    if(this.usedSize == this.elem.length){
        return true;
    }
        return false;
    }

    //在pos位置新增元素
    public void add(int pos, int data) {
        //pos合法性判断
        if(pos<0 || pos>this.usedSize){
            //位置不合法就抛出异常
            throw new PosOutOfBoundsException("位置不合法");
            //System.out.println("位置不合法");
            //return;
        }
        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++;
    }


    //判断是否包含某个元素
    public boolean contains(int toFind) {
        for (int i=0;i<this.usedSize;i++) {
            if (this.elem[i] == toFind){
                return true;
            }
        }
        return  true;
    }

    //查找某个元素对应位置
    public int indexOf(int toFind) {
        for (int i=0;i<this.usedSize;i++) {
            if (this.elem[i] == toFind){
                return i;
            }
        }

        return -1;
    }

    //获取pos位置的元素
    public int get(int pos) {
        if(pos<0 || pos >= this.usedSize) {
            //位置不合法就抛出异常
            throw new PosOutOfBoundsException("位置不合法");
        }
        return this.elem[pos];
    }

    //给pos位置的元素设为value 或者叫更新
    public void set(int pos, int value) {
        checkPos(pos);
        this.elem[pos] = value;
    }
    public void checkPos(int pos) {
        if (pos < 0 || pos >= this.usedSize) {
            //位置不合法就抛出异常
            throw new PosOutOfBoundsException("位置不合法");
        }
    }

    //删除第一次出现的关键字Key
    public void remove(int toRemove) {
        int index = indexOf(toRemove);
        if(index == -1){
            System.out.println("没有这个数据");
        return;
        }
        for (int i=index;i<this.usedSize-1;i++){
            this.elem[i] = this.elem[i+1];
        }
        //最后因为是从后往前移,少一个元素,我们用--来更新usedsized
        this.usedSize--;
    }

    //获取顺序表的长度
    public int size() {
        return this.usedSize;
    }

    //清空顺序表
    public void clear() {
        //面对引用数据类型
        /*for (int i=0;i<this.usedSize;i++){
            this.elem[i]=null;
        }*/
        this.usedSize=0;
    }

    //打印顺序表
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.println(this.elem[i] + "");
        }
    }
}
PosOutOfBoundsException.java
public class PosOutOfBoundsException extends RuntimeException {
    public PosOutOfBoundsException() {
    }

    public PosOutOfBoundsException(String message) {
        super(message);
    }
}

Test.java


public class Test {
    public static void main(String[] args) {
        MyArrayList myArrayList = new MyArrayList();
        myArrayList.add(1);
        myArrayList.add(2);
        myArrayList.add(3);
        myArrayList.add(4);
        myArrayList.add(5);
        /*myArrayList.add(3,199);
        System.out.println(myArrayList.contains(1));
        System.out.println(myArrayList.indexOf(10));*/
        /*System.out.println(myArrayList.get(1));
        System.out.println(myArrayList.get(3));
        System.out.println(myArrayList.get(5));*/
        /*myArrayList.set(0,100);*/
        myArrayList.remove(1);
        myArrayList.remove(5);
        myArrayList.remove(21312);
        myArrayList.display();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值