最近正准備回顧一下Java,所以在此做一些記錄。
1.LinkedList使用的是鏈表結構,先看一下節點的定義

1 /** 2 * 3 * 連接的節點 4 */ 5 private static class Node<E> { 6 //保存的數據 7 E item; 8 //后置節點 9 Node<E> next; 10 //前置節點 11 Node<E> prev; 12 //構造方法 13 Node(Node<E> prev, E element, Node<E> next) { 14 this.item = element; 15 this.next = next; 16 this.prev = prev; 17 } 18 }
2.add(E e) 添加一個元素

1 /** 2 * 添加一個元素 3 * 4 * @param e 添加的元素 5 */ 6 public boolean add(E e) { 7 //調用添加到末尾 8 linkLast(e); 9 return true; 10 } 11 12 13 /** 14 * 添加一個元素到鏈表的尾部 15 */ 16 void linkLast(E e) { 17 //獲取鏈表的尾部節點 18 final Node<E> l = last; 19 //創建新的節點,前置節點為last,后置節點為空 20 final Node<E> newNode = new Node<>(l, e, null); 21 //重新設置尾部節點 22 last = newNode; 23 //如果之前的尾部節點為空,說明之前沒有元素 24 if (l == null) 25 //設置頭節點也為空 26 first = newNode; 27 else 28 //如果之前的尾部節點不為空,則為之前的尾部節點追加后置節點 29 l.next = newNode; 30 //數量加一 31 size++; 32 modCount++; 33 } 34
3.add(int index, E element) 添加一個元素到指定位置

1 /** 2 * 添加一個元素到指定位置 3 * 4 * @param index 指定的位置 5 * @param element 添加的元素 6 */ 7 public void add(int index, E element) { 8 //檢查指定的位置是否越界 9 checkPositionIndex(index); 10 //如果插入的是尾部,直接調用插入尾部的方法 11 if (index == size) 12 linkLast(element); 13 else 14 //需要先找到指定位置的節點 15 // 插入到該節點之前 16 linkBefore(element, node(index)); 17 } 18 19 //檢查是否越界 20 private void checkPositionIndex(int index) { 21 if (!isPositionIndex(index)) 22 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 23 } 24 25 private boolean isPositionIndex(int index) { 26 return index >= 0 && index <= size; 27 } 28 29 /** 30 * 查詢指定位置的節點 31 */ 32 Node<E> node(int index) { 33 // 類似於二分查詢 34 // 如果位置小於集合大小的一半,從頭節點開始遍歷 35 // 否則從尾部節點開始遍歷 36 37 if (index < (size >> 1)) { 38 Node<E> x = first; 39 for (int i = 0; i < index; i++) 40 x = x.next; 41 return x; 42 } else { 43 Node<E> x = last; 44 for (int i = size - 1; i > index; i--) 45 x = x.prev; 46 return x; 47 } 48 } 49 50 /** 51 * 在某個節點之前進行插入 52 */ 53 void linkBefore(E e, Node<E> succ) { 54 // 獲取當前節點的前置節點 55 final Node<E> pred = succ.prev; 56 //保存創建的新節點 57 final Node<E> newNode = new Node<>(pred, e, succ); 58 //將新節點設置為當前位置節點的前置節點 59 succ.prev = newNode; 60 //如果當前位置節點的前置節點為空,則說明當前位置為頭節點 61 if (pred == null) 62 //重新設置頭節點 63 first = newNode; 64 else 65 //將當前位置節點的前置節點的后置節點設置為新節點 66 pred.next = newNode; 67 //數量增加 68 size++; 69 modCount++; 70 }
4.get(int index) 獲取指定位置的元素

1 /** 2 * 獲取指定位置的元素 3 * 4 * @param index 指定的位置 5 * @return 查詢到的元素 6 */ 7 public E get(int index) { 8 //判斷是否越界 9 checkElementIndex(index); 10 //查詢到節點的值 11 return node(index).item; 12 } 13 14 //檢查是否越界 15 private void checkElementIndex(int index) { 16 if (!isElementIndex(index)) 17 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 18 } 19 20 private boolean isElementIndex(int index) { 21 return index >= 0 && index < size; 22 } 23 24 /** 25 * 查詢指定位置的節點 26 */ 27 Node<E> node(int index) { 28 // 類似於二分查詢 29 // 如果位置小於集合大小的一半,從頭節點開始遍歷 30 // 否則從尾部節點開始遍歷 31 32 if (index < (size >> 1)) { 33 Node<E> x = first; 34 for (int i = 0; i < index; i++) 35 x = x.next; 36 return x; 37 } else { 38 Node<E> x = last; 39 for (int i = size - 1; i > index; i--) 40 x = x.prev; 41 return x; 42 } 43 }
5.remove(Object o) 刪除指定的元素

1 /** 2 * 刪除指定的元素 3 * 4 * @param o 需要刪除的元素 5 */ 6 public boolean remove(Object o) { 7 //判斷需要刪除的元素是否為空 8 if (o == null) { 9 //如果為空,從頭節點開始遍歷 10 for (Node<E> x = first; x != null; x = x.next) { 11 if (x.item == null) { 12 //刪除節點 13 unlink(x); 14 return true; 15 } 16 } 17 } else { 18 //刪除的元素不為為空,也是頭節點開始遍歷 19 for (Node<E> x = first; x != null; x = x.next) { 20 if (o.equals(x.item)) { 21 //刪除節點 22 unlink(x); 23 return true; 24 } 25 } 26 } 27 return false; 28 } 29 30 /** 31 * 刪除一個節點 32 */ 33 E unlink(Node<E> x) { 34 // 獲取刪除節點的值 35 final E element = x.item; 36 //獲取刪除節點的后置節點 37 final Node<E> next = x.next; 38 //獲取刪除節點的前置節點 39 final Node<E> prev = x.prev; 40 41 //判斷前置節點是否為空 42 if (prev == null) { 43 //為空則其后置節點晉升為頭節點 44 first = next; 45 } else { 46 //修改前置節點的后置節點 47 prev.next = next; 48 //將刪除節點的前置節點置空 49 x.prev = null; 50 } 51 52 //判斷后置節點是否為空 53 if (next == null) { 54 //后置節點為空,則其前置節點修改為尾部節點 55 last = prev; 56 } else { 57 //后置節點的前置節點修改 58 next.prev = prev; 59 //刪除節點的后置節點置空 60 x.next = null; 61 } 62 //刪除節點的值設置為空 63 x.item = null; 64 //數量減1 65 size--; 66 modCount++; 67 return element; 68 }
6.set(int index, E element) 在指定位置節點設置值

1 /** 2 * 在指定位置節點設置值 3 * 4 * @param index 指定的位置 5 * @param element 元素值 6 */ 7 public E set(int index, E element) { 8 //檢查位置是否越界 9 checkElementIndex(index); 10 //查詢到當前位置的節點 11 Node<E> x = node(index); 12 //獲取舊值 13 E oldVal = x.item; 14 //設置新值 15 x.item = element; 16 //返回舊值 17 return oldVal; 18 } 19 20 /** 21 * 查詢指定位置的節點 22 */ 23 Node<E> node(int index) { 24 // 類似於二分查詢 25 // 如果位置小於集合大小的一半,從頭節點開始遍歷 26 // 否則從尾部節點開始遍歷 27 28 if (index < (size >> 1)) { 29 Node<E> x = first; 30 for (int i = 0; i < index; i++) 31 x = x.next; 32 return x; 33 } else { 34 Node<E> x = last; 35 for (int i = size - 1; i > index; i--) 36 x = x.prev; 37 return x; 38 } 39 }
7.indexOf(Object o) 查詢指定元素值所在位置,lastIndexOf也一樣,只是從尾部節點開始查詢

1 /** 2 * 查詢指定元素值所在位置 3 * 4 * @param o 元素值 5 */ 6 public int indexOf(Object o) { 7 int index = 0; 8 //判斷查詢的值是否為null 9 if (o == null) { 10 //從頭節點開始遍歷查詢 11 for (Node<E> x = first; x != null; x = x.next) { 12 if (x.item == null) 13 return index; 14 index++; 15 } 16 } else { 17 //從頭節點開始遍歷查詢 18 for (Node<E> x = first; x != null; x = x.next) { 19 if (o.equals(x.item)) 20 return index; 21 index++; 22 } 23 } 24 return -1; 25 }
8.peek() 獲取頭節點的元素值,但不刪除頭節點
poll() 獲取頭節點的元素值,並刪除頭節點
pop() 獲取頭節點的元素值,並刪除頭節點,頭節點為空則拋出異常

1 /** 2 * 獲取頭節點的元素值,但不刪除頭節點 3 * 4 */ 5 public E peek() { 6 final Node<E> f = first; 7 return (f == null) ? null : f.item; 8 } 9 10 /** 11 * 獲取頭節點的元素值,並刪除頭節點 12 */ 13 public E poll() { 14 final Node<E> f = first; 15 return (f == null) ? null : unlinkFirst(f); 16 } 17 18 /** 19 * 刪除頭節點 20 */ 21 private E unlinkFirst(Node<E> f) { 22 //獲取節點的值 23 final E element = f.item; 24 //獲取到后置節點 25 final Node<E> next = f.next; 26 //清空節點的值和后置節點 27 f.item = null; 28 f.next = null; 29 //重新設置頭節點 30 first = next; 31 //判斷后置節點是否為null 32 if (next == null) 33 //說明只有這么一個節點,尾部節點設置null 34 last = null; 35 else 36 //已經設置為頭節點了,所以清空前置節點 37 next.prev = null; 38 //數量減1 39 size--; 40 modCount++; 41 return element; 42 } 43 44 /** 45 * 獲取頭節點的元素值,並刪除頭節點 46 */ 47 public E pop() { 48 return removeFirst(); 49 } 50 51 /** 52 * 刪除頭節點 53 */ 54 public E removeFirst() { 55 final Node<E> f = first; 56 //頭節點為空則拋出異常 57 if (f == null) 58 throw new NoSuchElementException(); 59 //刪除頭節點 60 return unlinkFirst(f); 61 }
9.offer(E e) 添加新元素到末尾
push(E e) 添加新元素到頭節點

1 /** 2 * 添加新元素到尾部 3 * 4 * @param e 新元素 5 */ 6 public boolean offer(E e) { 7 return add(e); 8 } 9 10 11 /** 12 * 添加一個元素 13 * 14 * @param e 添加的元素 15 */ 16 public boolean add(E e) { 17 //調用添加到末尾 18 linkLast(e); 19 return true; 20 } 21 22 /** 23 * 添加一個元素到鏈表的尾部 24 */ 25 void linkLast(E e) { 26 //獲取鏈表的尾部節點 27 final Node<E> l = last; 28 //創建新的節點,前置節點為last,后置節點為空 29 final Node<E> newNode = new Node<>(l, e, null); 30 //重新設置尾部節點 31 last = newNode; 32 //如果之前的尾部節點為空,說明之前沒有元素 33 if (l == null) 34 //設置頭節點也為空 35 first = newNode; 36 else 37 //如果之前的尾部節點不為空,則為之前的尾部節點追加后置節點 38 l.next = newNode; 39 //數量加一 40 size++; 41 modCount++; 42 } 43 44 /** 45 * 添加新元素到頭節點 46 * 47 * @param e the element to push 48 * @since 1.6 49 */ 50 public void push(E e) { 51 addFirst(e); 52 } 53 54 55 /** 56 * 添加元素到頭節點 57 * 58 * @param e 新增的元素 59 */ 60 public void addFirst(E e) { 61 //添加元素到頭節點 62 linkFirst(e); 63 } 64 65 /** 66 * 添加元素到頭節點 67 */ 68 private void linkFirst(E e) { 69 //獲取當前的頭節點 70 final Node<E> f = first; 71 //創建新節點 72 final Node<E> newNode = new Node<>(null, e, f); 73 //設置新節點為頭節點 74 first = newNode; 75 //判斷頭節點是否為空 76 if (f == null) 77 //頭節點為空,說明之前沒有節點,新節點同時為尾部節點 78 last = newNode; 79 else 80 //頭節點不為空,則頭節點的前置節點指向新節點 81 f.prev = newNode; 82 //數量加一 83 size++; 84 modCount++; 85 }
總結一下
1.LinkedList作為雙向鏈表,維護了頭尾節點,頭尾節點的插入比較方便,中間數據的插入需要遍歷查詢再做插入
2.查詢指定位置時,使用了一次二分,最多需要遍歷一半的節點