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。

最後不知道大家有沒有注意到,在列表聲明的時候,如果能預先知道數組的大小,不妨在聲明的時候,就將容量設置好,這樣就避免了數組擴容導致數組大量數據拷貝,進而引發性能問題。

不知各位看官對列表是否有一定的瞭解,如有疑問,歡迎留言,如有不當之處,請多多包涵和指教!

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