1.1 线性表——列表

什么是列表

列表是一种数据项构成的有限序列,即按照一定的线性顺序,排列而成的数据项的集合,在这种数据结构上进行的基本操作包括对元素的的查找,插入,和删除。

列表的两种主要表现是数组和链表,栈和队列是两种特殊类型的列表。

学过 C 语言的同学基本都知道,我们在学习列表的时候会定义一个结构体,然后对这个结构体进行各种操作,由于阿导后期从事 java 这条路,C 语言与我渐行渐远,所以下面阿导会通过 java 语言来阐述列表。

结构

对于列表而言,一般来说只需要两个字段,第一是存储数据的字段,这里用数组进行表示;第二是记录数据的数量字段。由于 java 中数组的长度需要提前声明,因此为了防止数组长度不够,我们需要添加一个辅助字段来判断是否需要对数组进行扩容,那么一个比较简单的列表数据结构就出来了,如下:


public class DaoList<E> {
    /**
     * 存储列表内容
     */
    private Object[] data;
    /**
     * 存储列表大小
     */
    private int size = 0;
    /**
     * 存储列表容量
     */
    private int length = 5;
    
}


数组的结构

列表的数据结构相对是比较简单的,当然仅仅只是声明了结构是无法将其用到实际开发当中去,下面阿导给出一些对列表常见的操作。

操作

  • 添加元素:public boolean add(E e)

添加元素

添加元素,首先需要判断存储元素的数组容量是否够用,然后将元素添加到数组末尾,元素大小加一即可。

  • 插入元素:public boolean add(int index,E e)

插入元素

插入元素,第一步要判断插入的位置是否合理,然后腾出插入的位置(如何保证数据不丢失才是关键),最后将元素插入到指定的位置即可,另外元素大小加一。

  • 修改元素:public void set(E e,int index)

首先需要查找到需要修改元素的下标,若存在则覆盖旧值,否则不做任何操作。

  • 判断指定元素是否存在:public int indexOf(E e) 或 public boolean contains(E e)

这个比较简单,只需要遍历数组,然后查询到返回下标,若查询不到返回 -1,若不需要返回下标,只需要返回存在与否,使用 contains(E e) 更为合适。

  • 获取指定下标的元素:public E get(int index)

首先判断下标是否越界,然后直接将对应的下标元素返回即可

  • 元素是否为空:public boolean isEmpty()

这里直接通过判断其元素大小是否为空就完了。

  • 删除元素:public E remove(E e) 或 public E remove(int index)

删除元素

首先需要判断其元素的下标是否存在,若存在则删除,并将数组数据向前移动到删除的位置,这里需要注意的是如何保证数据覆盖过度的问题。

  • 清空所有元素:public void clear()

通过遍历将数组每个下标一一置空,然后将元素大小置为0。

  • 转成数组:public Object[] toArray()

这里需要注意的是不要将数组原样返回,因为数组是引用类型,为了保证数据安全性,需要拷贝一个副本给调用方。

  • 遍历元素:public Iterator iterator()

这个可以直接借助 Iterator ,通过一个内部类实现其 hasNext() 和 next() 方法即可,有兴趣自己写迭代器的可参照阿导之前写过的【迭代器模式】。

完整示例


package com.dao.datastructure.list;

import java.util.Iterator;

/**
 * 线性表-列表
 *
 * @author 阿导
 * @CopyRight 万物皆导
 * @Created 2019-11-18 13:47:00
 */
public class DaoList<E> {
    /**
     * 存储列表内容
     */
    private Object[] data;
    /**
     * 存储列表大小
     */
    private int size = 0;
    /**
     * 存储列表容量
     */
    private int length = 5;

    /**
     * 默认表长度给五
     *
     * @return
     * @author 阿导
     * @time 2019/11/18 :00
     */
    public DaoList() {
        this(5);
    }

    /**
     * 指定长度
     *
     * @param length
     * @return
     * @author 阿导
     * @time 2019/11/18 :00
     */
    public DaoList(int length) {
        if (length < 0) {
            length = this.length;
        } else {
            this.length = length;
        }

        this.data = new Object[length];
    }

    /**
     * 添加元素,将指定元素添加到列表尾部
     *
     * @param e
     * @return boolean
     * @author 阿导
     * @time 2019/11/18 :00
     */
    public boolean add(E e) {
        this.expansion(1);
        this.data[this.size++] = e;
        return true;
    }
    /**
     * 将元素添加到指定位置
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param index
     * @param e
     * @return boolean
     */
    public boolean add(int index,E e){
        // 下标越界判断
        this.arrayIndexOutOfBoundsException(index);
        // 是否需要进行扩容
        this.expansion(1);
        // 后移一位,留出当前位置插入
        int pox = this.size++;
        while (pox>index){
            this.data[pox] = this.data[--pox];
        }
        this.data[index] = e;
        return true;
    }

    /**
     * 判断是否
     *
     * @param e
     * @return int
     * @author 阿导
     * @time 2019/11/18 :00
     */
    public int indexOf(E e) {
        // 当前指针
        int pox = 0;
        // 遍历其大小
        while (pox < this.size) {
            // 第一步要对 null 进行单独处理,防止空指针,然后判断是否相等
            if((e == null && this.data[pox] == null)||
            e!=null && e.equals(this.data[pox])){
                return pox;
            }
            pox++;
        }

        // 未查询到返回 -1
        return -1;
    }
    /**
     * 是否包含某个元素
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param e
     * @return boolean
     */
    public boolean contains(E e){
        return this.indexOf(e)>-1;
    }
    /**
     * 判断是否为空
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @return boolean
     */
    public boolean isEmpty(){
        return this.size==0;
    }
    /**
     * 返回当前列表大小
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @return int
     */
    public int size(){
        return this.size;
    }
    /**
     * 转化成数组
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @return E[]
     */
    public Object[] toArray(){
        Object[] dataTemp = new Object[this.size];
        System.arraycopy(this.data, 0, dataTemp, 0, this.data.length);
        return dataTemp;
    }

    /**
     * 获取指定位置元素
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param index
     * @return E
     */
    public E get(int index){
        // 下标越界判断
        this.arrayIndexOutOfBoundsException(index);
        // 获取指定位置数据
        return this.getData(index);
    }


    /**
     * 获取指定位置数据
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param index
     * @return E
     */
    private E getData(int index) {
        return (E)this.data[index];
    }

    /**
     * 若下标越界,抛出异常即可
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @return void
     * @param index
     */
    private void arrayIndexOutOfBoundsException(int index) {
        if(index>=this.size||index<0){
            throw new ArrayIndexOutOfBoundsException("数组越界!!!");
        }
    }
    /**
     * 指定位置设置值
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param e
     * @param index
     * @return boolean
     */
    public void set(E e,int index){
        this.arrayIndexOutOfBoundsException(index);
        this.data[index]=e;
    }

    /**
     * 移除某个位置的元素
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param index
     * @return E
     */
    public E remove(int index){
        this.arrayIndexOutOfBoundsException(index);
        // 删除之前获取元素
        E e = this.getData(index);
        // 然后将数组下标向前移一位
        int pox=index;
        while (pox<this.size-1){
            this.data[pox]=this.data[++pox];
        }
        // 末尾置空
        this.data[--this.size]=null;
        // 返回指定的元素
        return e;
    }

    /**
     * 移除某个元素
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @param e
     * @return boolean
     */
    public E remove(E e){
        int index = this.indexOf(e);
        // 未查询到不做任何处理,直接返回
        if(index==-1){
            return e;
        }
        // 移除吧
        this.remove(index);
        // 继续执行,直到里面所有的元素都删除
        return remove(e);
    }



    /**
     * 清空
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @return boolean
     */
    public void clear(){
        int pox=0;
        // 遍历置空
        while (pox<this.size){
            this.data[pox]=null;
        }

        this.size=0;
    }


    /**
     * 判断是否需要进行扩容,扩容方案有很多,直接向右移1个位加上目前的长度
     *
     * @return void
     * @author 阿导
     * @time 2019/11/18 :00
     */
    private void expansion(int num) {
        // 之所以判断是否需要扩容,是判断其需要添加的元素数量和当前大小之和是否大于当前长度
        if (this.size+num > this.length) {
            this.length = this.size+num + (this.length >> 1);
            //数组动态态扩容
            Object[] dataTemp = new Object[this.length];
            //数组复制
            System.arraycopy(this.data, 0, dataTemp, 0, this.data.length);
            //改变引用
            this.data = dataTemp;
        }
    }

    /** 
     * 迭代器
     *
     * @author 阿导
     * @time 2019/11/18 :00
     * @return java.util.Iterator<E>
     */
    public Iterator<E> iterator(){
        /**
         * 创建一个局部内部类,实现Iterator
         */
        class MyIterator implements Iterator<E>{
            /**
             * 数组的索引
             */
            private int index;
            /** 
             * 检查是否有下一个元素
             *
             * @author 阿导
             * @time 2019/11/18 :00
             * @return boolean
             */
            @Override
            public boolean hasNext() {
                return index < size ? true : false;
            }

            /**m
             * 获取数据
             * @return
             */
            @Override
            public E next() {
                return getData(this.index++);
            }
        }
        return new MyIterator();
    }

}


后话

离别总是伤感的,列表的阐述已告一段落,其实在 java 中,我们都清楚并熟练的使用列表 List,其实现类有很多,其中底层和阿导实现方式相同的常见的类是 ArrayList、Vector,这两个类也是很多面试官特别喜欢问的,其实这两个实现类主要区别是 Vector 做了线程同步处理,其它的实现基本一致,因此在多线程的环境下建议使用 Vector,反之使用 ArrayList。

最后不知道大家有没有注意到,在列表声明的时候,如果能预先知道数组的大小,不妨在声明的时候,就将容量设置好,这样就避免了数组扩容导致数组大量数据拷贝,进而引发性能问题。

不知各位看官对列表是否有一定的了解,如有疑问,欢迎留言,如有不当之处,请多多包涵和指教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章