什麼是列表
列表是一種數據項構成的有限序列,即按照一定的線性順序,排列而成的數據項的集合,在這種數據結構上進行的基本操作包括對元素的的查找,插入,和刪除。
列表的兩種主要表現是數組和鏈表,棧和隊列是兩種特殊類型的列表。
學過 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。
最後不知道大家有沒有注意到,在列表聲明的時候,如果能預先知道數組的大小,不妨在聲明的時候,就將容量設置好,這樣就避免了數組擴容導致數組大量數據拷貝,進而引發性能問題。
不知各位看官對列表是否有一定的瞭解,如有疑問,歡迎留言,如有不當之處,請多多包涵和指教!