1. 程式人生 > >java學習筆記--類ArrayList和LinkedList的實現

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

java 集合 list

在集合Collection下的List中有兩個實現使用的很頻繁,一個是ArrayList,另一個是LinkedList,在學習中肯定都會有這樣的疑問:什麽時候適合使用ArrayList,什麽時候用LinkedList?這時,我們就需要了解ArrayList和LinkedList的底層的實現,下面,為了更好的了解它們具體是怎樣實現的,我們來寫自己的ArrayList 和LinkedList。

ArrayList底層是基於數組實現的,數組在內存中是存儲在連續的存儲單元中,在數據查找的時候比較快,適用於不常進行插入數據和需要頻繁的查找數據的操作,下面,我們將其實現(為了方便理解,不使用泛型,用Object存儲數據):

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) {
		//要向一個數組中插入數據:1、數組的長度夠不夠  2、插入位置合不合法
		//如果插入位置不合法,拋出索引越界異常
		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;
				//1、container指向一個新容器
				container = new Object[size+1];
				//將原數組的下標從0到index的元素復制到擴容後的容器中
				System.arraycopy(temp, 0, container, 0, index);
				//2、將index及其以後位置的數據整體向後移位
				for(int i = size ; i > index ; i--) {
					container[i] = temp[i-1];
				}
				//3、插入數據
				container[index] = element;
				//4、元素個數加一
				++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()。

寫好該類後,對該類進行測試:

技術分享

從實現結果看,我們基本實現了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;
	}
    
}

寫好代碼後,對代碼進行測試:

技術分享

以上是個人拙見,如有錯誤,請指出,謝謝!

本文出自 “劉紫兵的博客” 博客,請務必保留此出處http://zibing.blog.51cto.com/8177516/1954864

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