更新:以下代碼是初學鏈表後的簡單實現,時隔一年再看一下代碼。覺得有點慘不忍睹,測試了一下,標準庫的鏈表插入1000萬個元素用了10秒鐘,以下代碼花費的時間遠超標準庫。因爲如果要將元素插入到鏈表末尾,都需要遍歷整個鏈表。是個O(N)的算法。但實際上,大部分插入都是插入到最後(或是最前)。我們其實只需要維護一個tail節點就能解決這個問題。這樣時間複雜度就爲O(1)了。
public void add(T val)
{
if(head==null)
{
head = new Node(val);
tail = head;
}
else
{
tail.next = new Node(val);
tail = tail.next;
}
++size;
}
下面是LinkedList的簡單實現。
LinkedList的優點: 新項的插入和現有項的刪除花費常數時間。(前提是已有變動項的位置,不然還是要在索引上花費O(N) )
LinkedList的缺點: 很難索引現有項,對get操作的調用花費很多時間。
MyLinkedList作爲雙向鏈表,get()方法可以從頭結點或者尾節點開始。所以最壞的情況下需要遍歷 【N/2】 個元素。
代碼中changedTimes和itChangedTimes的作用: 如果獲取了一個迭代器之後修改它所對應的MyLinkedList對象,則讓該迭代器失效。
package list;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* MyLinkedList
* @author earayu
* @param <T>
*/
public class MyLinkedList<T> implements Iterable<T> {
//雙向鏈表的節點
private static class Node<T>{
public T data;
public Node<T> prevNode;
public Node<T> nextNode;
public Node(T data, Node<T> prevNode, Node<T> nextNode) {
this.data = data;
this.prevNode = prevNode;
this.nextNode = nextNode;
}
}
//定義鏈表的長度,修改次數,頭結點和尾節點。
private int size;
private int changedTimes;
private Node<T> firstNode;
private Node<T> lastNode;
/**
* 調用init()方法,初始化鏈表
*/
public MyLinkedList(){
init();
}
/**
* 初始化MyLinkedList,將長度,被改變次數,頭結點和尾節點都初始化。以免發生錯誤。
*/
private void init(){
size = 0;
changedTimes = 0;
firstNode = new Node<T>(null, null, null);
lastNode = new Node<T>(null, null, null);
firstNode.nextNode = lastNode;
lastNode.prevNode = firstNode;
}
/**
* 將鏈表清空,修改次數+1
*/
public void clear(){
size = 0;
changedTimes++;
firstNode = new Node<T>(null, null, null);
lastNode = new Node<T>(null, null, null);
firstNode.nextNode = lastNode;
lastNode.prevNode = firstNode;
}
public boolean isEmpty(){
return size() == 0;
}
/**
* 返回當前鏈表長度(不含頭尾節點)
* @return
*/
public int size(){
return size;
}
/**
* 將idx索引出節點的值設置成newData
* @param idx
* @param newData
* @return
*/
public T set(int idx, T newData){
Node<T> p = getNode(idx);
T oldVal = p.data;
p.data = newData;
return oldVal;
}
/**
* 刪除制定索引出的節點,索引從0開始
* @param idx
* @return
*/
public T remove(int idx){
return remove(getNode(idx));
}
private T remove(Node<T> p){
p.nextNode.prevNode = p.prevNode;
p.prevNode.nextNode = p.nextNode;
--size;
++changedTimes;
return p.data;
}
/**
* 將data添加到鏈表尾部。
* @param data
* @return
*/
public boolean add(T data){
add(size, data);
return true;
}
/**
* 將鏈表添加到idx索引出,索引從0開始
* @param idx
* @param data
* @return
*/
public boolean add(int idx, T data){
if(idx < 0 || idx > size){
throw new IndexOutOfBoundsException();
}
Node<T> beforeNode = firstNode;
for(int i=0;i<size;i++){
beforeNode = beforeNode.nextNode;
}
Node<T> afterNode = beforeNode.nextNode;
beforeNode.nextNode = new Node(data, beforeNode, afterNode);
afterNode.prevNode = beforeNode.nextNode;
++size;
++changedTimes;
return true;
}
/**
* 獲取idx索引出的節點的數據,索引從0開始
* @param idx
* @return
*/
public T get(int idx){
return getNode(idx).data;
}
/**
* 獲取idx索引出的節點
* @param idx
* @return
*/
private Node<T> getNode(int idx){
if(idx < 0 || idx >= size){
throw new IndexOutOfBoundsException();
}
Node<T> p;
if(idx < size/2){
p = firstNode.nextNode;
for(int i=0; i<idx; i++){
p = p.nextNode;
}
}else{
p = lastNode;
for(int i=size; i>idx; i--){
p = p.prevNode;
}
}
return p;
}
/**
* 返回一個迭代器
*/
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
/**
*
* @author earayu
*
*/
private class LinkedListIterator implements Iterator<T>{
//迭代器的當前指向節點,修改次數,可否移除
private Node<T> currentNode = firstNode.nextNode;
private int itChangedTimes = changedTimes;
private boolean removable = false;
/**
* 返回是否有下一個節點(不包含尾節點)。
*/
@Override
public boolean hasNext() {
return currentNode != lastNode;
}
/**
* 返回當前指向節點的下一個節點的值,並將指向的節點後移一位。
*/
@Override
public T next() {
if(changedTimes != itChangedTimes){
throw new ConcurrentModificationException();
}
if(!hasNext()){
throw new NoSuchElementException();
}
T nextItem = currentNode.data;
currentNode = currentNode.nextNode;
removable = true;
return nextItem;
}
/**
* 調用外部類的remove方法,移除上一個訪問過的節點。即,當前指向節點的前一個節點。
*/
public void remove(){
if(changedTimes != itChangedTimes){
throw new ConcurrentModificationException();
}
if(!removable){
throw new IllegalStateException();
}
MyLinkedList.this.remove(currentNode.prevNode);
removable = false;
++itChangedTimes;
}
}
}