java學習筆記--類ArrayList和LinkedList的實現

    在集合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

wKioL1mKy1ugjAkzAAIN-C4xw64618.jpg-wh_50

從實現結果看,咱們基本實現了ArrayList的功能,此處重要的兩個方法是插入數據和移除數據,固然本程序也有bug,就是remove方法。學習

    LinkedList底層是基於鏈表實現的,在數據插入和刪除時速度較快,適用於頻繁進行插入和刪除的操做,wKioL1mK0rzBlD1pAABi31t26BE135.jpg-wh_50這裏咱們實現一個單鏈表:
測試

/**
 * 自定義鏈式存儲列表 :單鏈表
 * @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

wKiom1mK0hCBkqa2AAItEh1xheo045.jpg-wh_50

以上是我的拙見,若有錯誤,請指出,謝謝!對象

相關文章
相關標籤/搜索