package com.zkn.newlearn.collection;
/**
*
* @author zkn 2016-06-25
* LinkedList的內部資料結構是雙向連結串列,
* 所以定義一個內部類,用來表示一個節點,
* 這個節點包括三個屬性,
* 1、一個用來表示當前元素
* 2、一個用來表示上一個元素
* 3、一個用來表示下一個元素
* 還需要兩個屬性節點用來儲存連結串列的頭和尾
*/
public class ImitateLinkedListTest02<E> {
/**
* 表示頭部
*/
private Node<E> first;
/**
* 表示尾部
*/
private Node<E> last;
/**
* size的大小
*/
private int size;
/**
* 元素的個數
*/
public int size(){
return size;
}
/**
* add的方法
* @param e
*/
public void add(E e){
//說明這個時候還沒有進行過add操作,即連結串列中沒有元素
if(last == null){
//建立一個新的節點
//對於第一個節點,它的上一個元素為不存在所以為null,下一個元素同樣為null
Node<E> newNode = new Node<E>(e,null,null);
//在這個連結串列中,第一個元素為當前要插入的節點,最後一個元素同樣為當前要插入的節點
first = newNode;
last = newNode;
}else{
//對於連結串列中已經存在元素節點的情況
//建立出來一個新的節點
//對於這種連結串列中已經有幾點存在的情況,它的上一個節點為最後一個元素,下一個節點為null
Node<E> newNode = new Node<E>(e,last,null);
//當前的最後一個元素的下一個節點應該指向當前要插入的這個節點。
//當前最後一個元素的下一個節點指向當前要插入的節點,當前要插入的節點的上一個元素指向當前的最後一個元素
//這樣正好構成了一個雙向連結串列
last.next = newNode;
//最後,要把最後一個元素節點變為當前插入的元素節點
last = newNode;
}
size++;
}
/**
* 根據元素位置取元素的值
* 從這個例子中就可以看出來,為什麼LinkedList獲取元素比較慢,因為每次取出元素都有進行一次迴圈!!!!
* @param size
* @return
*/
public E get(int index){
checkSize(index);
//獲取元素的值
//如果索引小於當前元素個數的一半,就從頭部開始迴圈,否則從尾部開始迴圈
if(index < (size >> 1)){
//把頭給節點,便於下面遞迴迴圈
Node<E> node = first;
for(int i=0;i<index;i++){
//迴圈遞迴
node = node.next;
}
//返回節點中的元素
return node.item;
}else{
//把尾給節點,便於下面遞迴迴圈
Node<E> node = last;
for(int i=size-1;i>index;i--){
node = node.prev;
}
return node.item;
}
}
/**
* 獲取第一個元素
* @return
*/
public E getFirst(){
if(first == null)
return null;
return first.item;
}
/**
* 獲取最後一個元素
* @return
*/
public E getLast(){
if(last == null)
return null;
return last.item;
}
/**
* 移除物件
* @return
*/
public boolean remove(Object obj){
//分兩種情況來處理
//如果obj == null
if(obj == null){
for(Node<E> x = first;x != null;x = x.next){
if(x.item == null){
removeElement(x);
return true;
}
}
}else{
for(Node<E> x = first;x != null;x = x.next){
if(obj.equals(x.item)){
removeElement(x);
return true;
}
}
}
return false;
}
private E removeElement(Node<E> node) {
E itemElement = node.item;
//用來儲存prev節點,防止後面 當node節點是最後一個節點的時候, node.prev=null,last為null
Node<E> prevNode = node.prev;
//說明node為first節點
if(node.prev == null){
//first節點的時候需要把node.next變為first
first = node.next;
}else{
//如果node不是first節點,則把他的上個節點的指向變成當前node的下一個節點
//然後把這個節點的上個節點的變為null 相當於打斷節點的左面
node.prev.next = node.next;
node.prev = null;
}
//如果node為last節點
if(node.next == null){
//說明node的上一個節點為last節點
last = prevNode;
}else{
//說明如果node不是last幾點,則把node節點的指向的下一個元素的上一個節點變為當前node的上一個節點
//然後把當前node節點的next變為null 相當於打斷節點的右面
node.next.prev = prevNode;
node.next = null;
}
node.item = null;
size --;
return itemElement;
}
/**
* 檢查元素是否合法
* @param size2
*/
private void checkSize(int index) {
if(index >= size || index < 0){
throw new IllegalArgumentException("輸入的引數不合法,請輸入合法的引數");
}
}
/**
* 雙向連結串列的節點
* @author zkn
*
* @param <E>
*/
private static class Node<E>{
//當前元素
E item;
//上一個
Node<E> prev;
//下一個
Node<E> next;
public Node(E item, Node<E> prev, Node<E> next) {
this.item = item;
this.prev = prev;
this.next = next;
}
}
public static void main(String[] args){
ImitateLinkedListTest02<String> linkedList = new ImitateLinkedListTest02<String>();
linkedList.add("張三");
linkedList.add("李四");
linkedList.add("馬六");
linkedList.add("王五");
linkedList.remove("馬六1");
System.out.println(linkedList.size());
System.out.println(linkedList.getFirst());
System.out.println(linkedList.getLast());
for(int i=0;i<linkedList.size;i++){
System.out.print(linkedList.get(i)+"->");
}
System.out.println("");
}
}