数据结构-线性表-模拟实现顺序表ArrayList

模拟实现顺序表ArrayList

1、定义接口

不管是顺序表仍是链表,它们都是线性表,都须要进行增删改查操做。
因此首先,定义一个线性表接口List,包含线性表的操做数组

/**
 * 线性表接口
 * 和存储结构无关(顺序表,链表)
 */
public interface List
{
    //返回线性表的大小,即元素的个数
    public int size();

    //返回线性表中序号为i 的数据元素
    public Object get(int i);

    //若是线性表为空,返回true,不然返回false
    public boolean isEmpty();

    //判断线性表中是否包含数据元素e
    public boolean contains(Object e);

    //返回数据元素e 在线性表中的序号
    public int indexOf(Object e);

    //将数据元素e 插入到线性表中i 号位置
    public void add(int i, Object e);

    //将数据元素e 插入到线性表末尾
    public void add(Object e);

    //将数据元素e 插入到元素obj 以前
    public boolean addBefore(Object obj, Object e);

    //将数据元素e 插入到元素obj 以后
    public boolean addAfter(Object obj, Object e);

    //删除线性表中序号为i 的元素,并返回之
    public Object remove(int i);

    //删除线性表中第一个与e 相同的元素
    public boolean remove(Object e);

    //替换线性表中序号为i 的数据元素为e, 返回原数据元素
    public Object replace(int i, Object e);
}

2、建立顺序表类ArrayList并实现接口List

一、顺序表底层采用的是数组,长度能够动态变化

private Object[] elementData; //底层是一个数组,目前尚未肯定长度
    private int size; //不是数组分配了几个空间,而是数组中元素的个数

二、编写构造方法指定数组初始长度

public ArrayList(int initalCapacity) {
        //给数组分配指定数量的空间
        elementData = new Object[initalCapacity];
        //指定顺序表的元素个数,默认是0
//        size = 0;
    }
public ArrayList() {
        //没有指定长度,默认长度是4
        this(4);
    }

以上代码写完,就能够建立顺序表了,可是里面的操做方法尚未实现安全

三、实现add方法,将数据元素e 插入到线性表中i 号位置,代码以下

public void add(int i, Object e) {
        //i 的位置要正确
        if (i<0 || i > size){
            throw new MyArrayIndexOutOfBoundsException("数组索引越界异常:"+i);
        }
        //数组元素个数等于数组长度时,须要扩容
        if(size == elementData.length){
            //扩容方法
            grow();
        }
        //后移i 及其后面的元素,从最后一个元素开始
        for (int j = size; j > i ; j--) {
            elementData[j] = elementData[j-1];
        }
        //给数组第i 个位置赋值
        elementData[i] = e;
        //数组元素个数+1
        size++;
    }

首先判断索引是否正确,不正确就抛出一个自定义异常app

public class MyArrayIndexOutOfBoundsException extends RuntimeException {
    public MyArrayIndexOutOfBoundsException() {
    }

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

而后当数组元素个数等于数组长度时,调用grow()方法进行扩容,grow()方法有两种实现ui

//基本思路是这样
    private void grow(){
        //建立一个新数组,长度是旧数组2倍
        Object[] newArr = new Object[elementData.length * 2];
        //将旧数组数据拷贝到新数组
        for (int i = 0; i < size; i++) {
            newArr[i] = elementData[i];
        }
        //让旧数组 指向 新数组
        elementData = newArr;
    }
    
    //一步到位
    private void grow(){
        //扩容 拷贝 赋值
        elementData = Arrays.copyOf(elementData, elementData.length * 2);
    }

最后实现添加操做:须要从数组尾部元素开始,将第i 位及其以后的元素向后移一位,而后把e 赋值给第i 个位置的元素,最后数组元素个数+1this

四、把元素添加到线性表末尾,是add()方法的特殊状况

public void add(Object e) {
        this.add(size, e);
    }

五、get()方法,返回线性表中序号为i 的元素,即数组中下标为i 的元素

public Object get(int i) {
        if (i < 0 || i >= size) {//i < 0 或者 i >= size
            throw new MyArrayIndexOutOfBoundsException("数组索引越界异常:" + i);
        }
        return elementData[i];
    }

判断索引是否正确,错误抛出自定义异常线程

六、remove(int i)方法,删除线性表中序号为i 的元素,并返回之

public Object remove(int i) {
        if (i < 0 || i >= size) {
            throw new MyArrayIndexOutOfBoundsException("数组越界异常:"+ i);
        }
        Object empt = elementData[i];
        for (int j = i; j < size; j++) {
            elementData[j] = elementData[j+1];
        }
        elementData[size-1] = null;
        size--;
        return empt;
    }

3、重写toString()方法

public String toString() {
        if (size == 0){
            return "[]";
        }
        StringBuilder builder = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            if (i != size - 1){
                builder.append(elementData[i]+",");
            }else {
                builder.append(elementData[i]);
            }
        }
        builder.append("]");
        return builder.toString();
    }

扩展

StringBuffer和StringBuilder很是相似,均表明可变的字符序列。 这两个类都是抽象类AbstractStringBuilder的子类,方法几乎如出一辙。
StringBuffer 线程安全,作线程同步检查, 效率较低。
StringBuilder 线程不安全,不作线程同步检查,所以效率较高。 建议使用该类。code

4、核心

数组扩容、元素前移、后移索引

ArrayList源码中扩容为50%接口

int newCapacity = oldCapacity + (oldCapacity >> 1);
相关文章
相关标签/搜索