在集合Collection下的List中有兩個實現使用的很頻繁,一個是ArrayList,另外一個是LinkedList,在學習中確定都會有這樣的疑問:何時適合使用ArrayList,何時用LinkedList?這時,咱們就須要瞭解ArrayList和LinkedList的底層的實現,下面,爲了更好的瞭解它們具體是怎樣實現的,咱們來寫本身的ArrayList 和LinkedList。java
ArrayList底層是基於數組實現的,數組在內存中是存儲在連續的存儲單元中,在數據查找的時候比較快,適用於不常進行插入數據和須要頻繁的查找數據的操做,下面,咱們將其實現(爲了方便理解,不使用泛型,用Object存儲數據):
node
import java.util.Arrays; /** * 順序存儲結構:類ArrayList的實現 * @author liuzb * 2017年8月8日 下午2:58:59 */ public class SequenceStroeLinearList{ /** * 用於存儲容器中實際存儲的元素個數 */ private int size = 0; /** * 底層用於存儲數據的容器 */ private Object[] container; /** * 在順序存儲結構中,用於初始化 */ private static final Object[] EMPTY_CONTAINER = {}; /** * 構造器,用於初始化存儲數據的容器 */ public SequenceStroeLinearList(){ this.container = EMPTY_CONTAINER; } /** * 構造器,用於初始化存儲數據的容器 */ public SequenceStroeLinearList(int minCapactiy) { if(minCapactiy > 0) { container = new Object[minCapactiy]; }else if(minCapactiy == 0) { container = EMPTY_CONTAINER; }else { throw new ArrayIndexOutOfBoundsException(); } } /** * 向容器中添加一個元素 * @param index 添加元素位置 * @param element 待添加元素 */ public void insert(int index ,Object element) { //要向一個數組中插入數據:一、數組的長度夠不夠 二、插入位置合不合法 //若是插入位置不合法,拋出索引越界異常 if(index > container.length || index <0) { throw new ArrayIndexOutOfBoundsException(); }else { //插入位置合法 //若是是在尾部插入數據 if(index == container.length) { container = Arrays.copyOf(container, ++size); container[index] = element; }else { //若是不是在尾部插入數據,先用臨時變量存儲容器中的內容 Object[] temp = container; //一、container指向一個新容器 container = new Object[size+1]; //將原數組的下標從0到index的元素複製到擴容後的容器中 System.arraycopy(temp, 0, container, 0, index); //二、將index及其之後位置的數據總體向後移位 for(int i = size ; i > index ; i--) { container[i] = temp[i-1]; } //三、插入數據 container[index] = element; //四、元素個數加一 ++size; } } } /** * 向容器中添加數據 * @param obj 須要添加的對象 */ public void add(Object obj) { insert(size,obj); } /** * 容器中實際存儲的元素個數 * @return */ public int size() { return size; } /** * 獲取指定索引位置的對象 * @param index 指定位置的索引 * @return 指定位置的對象 */ public Object get(int index) { if(index <0 || index > size) { throw new ArrayIndexOutOfBoundsException(); } return container[index]; } /** * 獲取指定對象的索引 * @param obj 須要獲取索引的對象 * @return 索引 */ public int indexOf(Object obj) { int index = -1; for(int i = 0; i<size ; i++) { if(container[i].equals(obj)) { index = i; } } return index; } /** * 容器中是否包含某個元素 * @param obj * @return false 不包含 true 包含 */ public boolean contains(Object obj) { return indexOf(obj) == -1 ? false :true; } /** * 從容器中移除指定索引的元素 * @param index 須要移除元素的索引 * @return 移除 */ public boolean remove(Integer index) { boolean flag = true; // 非法索引,拋出異常 if (index < 0 || index > size) { flag = false; throw new ArrayIndexOutOfBoundsException("移除指定索引元素失敗,索引值非法"); } else { // 索引合法 for (int i = index; i < size-1; i++) { //將index到size的元素依次往前移位 container[i] = container[i + 1]; } // 將末尾元素值賦爲 null container[size-1] = null; // 元素個數減一 -- size; } return flag; } /** * 移除指定元素 * @param obj 須要移除的元素 * @return true :移除成功 false:移除失敗 */ public boolean remove(Object obj){ if(contains(obj)) { remove(indexOf(obj)); return true; } return false; } }
在實現中,用到了util包下面的Arrays幫助類,此處也可使用System.arrayCpy()。數組
寫好該類後,對該類進行測試:
ide
從實現結果看,咱們基本實現了ArrayList的功能,此處重要的兩個方法是插入數據和移除數據,固然本程序也有bug,就是remove方法。學習
LinkedList底層是基於鏈表實現的,在數據插入和刪除時速度較快,適用於頻繁進行插入和刪除的操做,這裏咱們實現一個單鏈表:
測試
/** * 自定義鏈式存儲列表 :單鏈表 * @author liuzb * 2017年8月9日 上午11:24:07 */ public class MySingleLinkedList { /** * 單鏈表中的首節點 */ private Node header; /** * 單鏈表中的尾節點 */ private Node tail; /** * 單鏈表中實際存儲元素的個數 */ private int size; /** * 內部類,用於封裝節點須要的數據和下一個節點的地址 * @author liuzb * 2017年8月9日 上午11:24:43 */ private class Node{ /** * 當前鏈表存儲的數據 */ private Object data; /** * 當前節點存儲的下一個節點的地址 */ private Node next; /** * 無參構造器,用於節點的初始化 */ public Node() { } /** * 有參構造器,用於節點的初始化 * @param date 節點存儲的值 * @param next 節點中保存的下一個節點的地址 */ public Node(Object data,Node next) { this.data = data; this.next = next; } /** * 獲取當前節點的存儲的值 * @return 節點值 */ public Object getData() { return data; } } /** * 單鏈表頭插入法 * @param item 須要存儲的數據 */ public void addHeader(Object item) { //定義一個節點 Node node = null; //若是原鏈表是空表 if(size == 0) { //構建一個新的節點,節點的下一個節點指向null node = new Node(item,null); //頭結點和尾節點都指向新節點 header = node; tail = header; }else { //若是原鏈表不是空表,定義一個新節點,新節點的下一個節點指向原來的頭結點 node = new Node(item,header); //新節點變成了頭結點 header = node; } //元素的個數加一 size ++; } /** * 單鏈表爲插入法 * @param item 須要出入的元素 */ public void addLast(Object item) { //建立一個新節點 Node node = new Node(item,null); // 若是原來的鏈表是空表 if(size == 0) { //單鏈表的頭結點和尾節點都指向新節點 tail = header = node; }else { //原來的尾節點的下一個節點指向新節點 tail.next = node; //新節點變成了尾節點 tail = node; } //鏈表的元素個數加一 size ++; } /** * 向單鏈表中添加元素 * @param item 待添加的元素 * @return */ public void add(Object item) { //方法中默認使用尾插入法插入元素 addLast(item); } /** * 移除指定位置的元素 * @param index 須要移除元素的索引 * @return true:移除成功 false:移除失敗 */ public boolean remove(int index) { boolean flag = false; //若是索引非法,拋出異常 if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("移除元素的索引越界"); } else { //若是移除的是頭結點 if (index == 0) { //原頭結點的下一個節點變成了頭結點 header = header.next; }else if(index == size-1) { //若是刪除的是尾節點,先獲取原尾節點的前一個節點 Node node = getNodeByIndex(index-1); //將原尾節點的前一個節點存儲的下一個節點地址信息置爲null node.next = null; //原尾節點的前一個節點變成了尾節點 tail = node; }else { //刪除的既不是頭結點,也不是尾節點,將須要刪除的數據先暫時存儲 Node removeNode = getNodeByIndex(index); //獲取須要刪除數據的前一個節點 Node node = getNodeByIndex(index - 1); //將前一個節點的下一個節點指向須要刪除的節點的下一個節點 node.next = removeNode.next; } //元素個數減一 size -- ; flag = true; } return flag; } /** * 獲取指定索引的節點 * @param index 須要獲取的節點的索引值 * @return 節點對象 */ public Node getNodeByIndex(int index) { Node current = header; for(int i = 0;i < size && current != null;i++) { if(index == i) { return current; } current = current.next; } return null; } /** * 獲取單鏈表中元素個數 * @return 元素個數 */ public int size() { return size; } /** * 獲取指定索引的節點值 * @param index 須要獲取的節點的索引 * @return 節點值 */ public Object get(int index) { //索引非法,拋出異常 if(index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("沒法獲取該節點的值"); } return getNodeByIndex(index).getData(); } /** * 向鏈表指定位置插入數據 * @param index 待插入位置 * @param item 待插入數據 * @return true 插入成功 false 插入失敗 */ public boolean insert(int index,Object item) { boolean flag = true; if(index < 0 || index >size) { flag = false; throw new ArrayIndexOutOfBoundsException(); } //若是插入到頭結點前 if(index == 0) { header = new Node(item,header); }else if(index == size) { //插入到尾節點後 //定義一個新的節點 Node node = new Node(item,null); //尾節點的下一個節點指向新節點 tail.next = node; //新節點變成了尾節點 tail = node; }else { //在首節點和尾節點之間插入節點 //獲取出入位置的節點 Node indexNode = getNodeByIndex(index); //定義新節點,新節點的下一個節點指向原插入位置的節點 Node newNode = new Node(item,indexNode); //獲取插入位置的前一個節點 Node node = getNodeByIndex(index-1); //插入位置的前一個節點的下一個節點指向新節點 node.next = newNode; } //元素個數加一 size ++ ; return flag; } }
寫好代碼後,對代碼進行測試:this
以上是我的拙見,若有錯誤,請指出,謝謝!對象