JDK1.8集合框架原始碼分析二-------------LinkedList
0.LinkedList特點
0.1) 刪除或新增效率高:不會移動資料元素,只會維護內部節點的關係
0.2) 查詢慢: 在其內部沒有下標,也即沒有索引,需要使用for迴圈遍歷,LInkedList為了提高效率,採用折半查詢演算法
1.LinkedList的成員屬性
1.1 int size --- LinkedList容器現有的節點個數
1.2 Node<E> first;--- 首節點,目的是為了查詢方便,從裡可以發起查詢
1.3 Node<E> last--- 尾節點,目的: (1) 從這裡新增元素 (2)也可以從這裡發起查詢
2.LinkedList的Node類的資料結構
private static class Node<E>{ E data;//節點資料 Node<E> prev;//上一個節點 Node<E> next;//下一個節點 public Node(E data, Node<E> prev, Node<E> next) { this.data = data; this.prev = prev; this.next = next; } }
3.LinkedList的資料結構
4.LinkedList新增元素
private void linkLast(E e) { //建立一個臨時變數儲存容器的尾節點 final Node<E> l = last; //建立一個以當前容器尾節點為上一個節點,null為下一個節點的新節點 final Node<E> newNode = new Node<E>(e, l, null); //把新節點賦值給尾節點 last = newNode; //如果首節點為空 if(first == null){ first = newNode; }else{ l.next = newNode; } size ++ ; } @Override public boolean add(E e) { linkLast(e); return true; }
5.LinkedList 指定下標index查詢元素
5.1) 陣列越界檢查 index >=0 && index < size
5.2) 根據當前容器所儲存的資料大小進行折半查詢
index < size >> 1 ====> index < size/2 從容器的首節點開始查詢
否則 從容器的尾節點開始查詢
@Override
public E get(int index) {
checkElementIndex(index);
return node(index).data;
}
private void checkElementIndex(int index) {
if (index >= 0 && index < size) {
return;
}
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private Node<E> node(int index) {
//即要想找到下標為index的節點,則只需要找到其相鄰節點即可得到該節點的資料
Node<E> node = null;
if(index < size >> 1){
node = first;
for (int i = 0; i < index; i ++){
node = node.next;
}
}else{
node = last;
for (int i = (size -1); i > index; i --){
node = node.prev;
}
}
return node;
}
6.LinkedList在指定下標index位置新增元素
6.1) 陣列下標越界檢查 index>=0 && index<=size
6.2) 找到下標所在的節點indexNode (index >=0 && index < size 只能找到這個範圍的節點)
6.3) 下標所在的節點為新節點newNode的下一個節點--->newNode.next = indexNode
6.4) 下標所在的節點的上一個節點indexOldPrevNode為新節點的上一個節點--->newNode.prev = indexOldPrevNode
6.4.1) 如果indexNode節點的上一個節點為null,則indexNode當前為first節點,此時需要更新首節點的內容
6.5)下標所在的節點的上一個節點的新值為新節點--->indexNode.prev = newNode
6.6 ) 當index = size時 ,直接在容器的末尾新增節點即可
private void linkBefore(E e, Node<E> indexNode) {
//下標所在的節點的上一個節點
Node<E> prev = indexNode.prev;
//利用建構函式建立新節點,
//新節點的上一個節點為下標所在節點的上一個節點
//新節點的下一個節點為下標所在的節點
Node<E> newNode = new Node<>(e,prev,indexNode);
//下標所在節點的新的上一個節點為新節點
indexNode.prev = newNode;
//如果下標所在節點的老的上一個節點為null
//則說明下標所在節點在插入新的節點之前為首節點
//現在插入新節點之後,新節點則稱為首節點
if(prev == null){
first = newNode;
}else{
//如果下標所在節點的老的上一個節點不為null
//則說明下標所在的節點在插入新的節點之前不是首節點
//則下標所在的節點的老的上一個節點的下一個節點為新節點
prev.next = newNode;
}
size++;
}
@Override
public boolean add(int index, E e) {
checkElementIndexInPos(index);
if (index == size){
linkLast(e);
}else{
linkBefore(e,node(index));
}
return true;
}
7.LinkedList指定下標刪除節點
7.1) 陣列越界檢查 index >=0 && index < size
7.2) 找出下標所在的節點 indexNode
7.3) indexNode.prev = oldIndexPrevNode; indexNode.next = oldIndexNextNode;
7.4) 如果 oldIndexPrevNode = null,則 first = indexNode ,即刪除的為首節點 此時 需要 first = oldIndexNextNode;
7.5) 如果 oldIndexPrevNode != null 則 oldIndexPrevNode.next = oldIndexNextNode;
7.6) 如果 oldIndexNextNode = null,則 last = indexNode,即刪除的是尾節點,需要last = oldIndexPrevNode;
7.7) 如果 oldIndexNextNode != null, 則oldIndexNextNode.prev = oldIndexPrevNode;
private E unlink(Node<E> indexNode) {
final E data = indexNode.data;
final Node<E> oldIndexPrevNode = indexNode.prev;
final Node<E> oldIndexNextNode = indexNode.next;
if(oldIndexPrevNode == null){
first = oldIndexNextNode;
}else{
oldIndexPrevNode.next = oldIndexNextNode;
indexNode.prev = null;//GC回收
}
if(oldIndexNextNode == null){
last = oldIndexPrevNode;
}else{
oldIndexNextNode.prev = oldIndexPrevNode;
indexNode.next = null;//GC回收
}
indexNode.data = null;//GC回收
size--;
return data;
}
@Override
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
8.LIinkedList指定元素值刪除
8.1) 找出指定元素所對應的節點
8.2) 根據節點刪除資料和步驟7一樣
@Override
public boolean remove(Object obj) {
if(obj == null){
//從首節點開始遍歷,找到複合條件的節點
for(Node<E> x = first; x.next !=null; x = x.next){
if(x.data == null){
unlink(x);
return true;
}
}
}else{
for(Node<E> x = first; x.next !=null; x = x.next){
if(obj.equals(x.data)){
unlink(x);
return true;
}
}
}
return false;
}
9.自己手寫LinkedList原始碼程式碼以及Junit測試類
package com.roger.collection.impl;
import com.roger.collection.RogerList;
public class RogerLinkedList<E> implements RogerList<E> {
//容器中資料的大小
private int size;
//容器中首節點
private Node<E> first;
//容器中尾節點
private Node<E> last;
private void linkLast(E e) {
//建立一個臨時變數儲存容器的尾節點
final Node<E> l = last;
//建立一個以當前容器尾節點為上一個節點,null為下一個節點的新節點
final Node<E> newNode = new Node<E>(e, l, null);
//把新節點賦值給尾節點
last = newNode;
//如果首節點為空
if (first == null) {
first = newNode;
} else {
l.next = newNode;
}
size++;
}
@Override
public boolean add(E e) {
linkLast(e);
return true;
}
private void linkBefore(E e, Node<E> indexNode) {
//下標所在的節點的上一個節點
Node<E> prev = indexNode.prev;
//利用建構函式建立新節點,
//新節點的上一個節點為下標所在節點的上一個節點
//新節點的下一個節點為下標所在的節點
Node<E> newNode = new Node<>(e,prev,indexNode);
//下標所在節點的新的上一個節點為新節點
indexNode.prev = newNode;
//如果下標所在節點的老的上一個節點為null
//則說明下標所在節點在插入新的節點之前為首節點
//現在插入新節點之後,新節點則稱為首節點
if(prev == null){
first = newNode;
}else{
//如果下標所在節點的老的上一個節點不為null
//則說明下標所在的節點在插入新的節點之前不是首節點
//則下標所在的節點的老的上一個節點的下一個節點為新節點
prev.next = newNode;
}
size++;
}
@Override
public boolean add(int index, E e) {
checkElementIndexInPos(index);
if (index == size){
linkLast(e);
}else{
linkBefore(e,node(index));
}
return true;
}
private void checkElementIndexInPos(int index) {
if (index >= 0 && index <= size) {
return;
}
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@Override
public E get(int index) {
checkElementIndex(index);
return node(index).data;
}
private void checkElementIndex(int index) {
if (index >= 0 && index < size) {
return;
}
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private Node<E> node(int index) {
//即要想找到下標為index的節點,則只需要找到其相鄰節點即可得到該節點的資料
Node<E> node = null;
if(index < size >> 1){
node = first;
for (int i = 0; i < index; i ++){
node = node.next;
}
}else{
node = last;
for (int i = (size -1); i > index; i --){
node = node.prev;
}
}
return node;
}
@Override
public int size() {
return size;
}
private E unlink(Node<E> indexNode) {
final E data = indexNode.data;
final Node<E> oldIndexPrevNode = indexNode.prev;
final Node<E> oldIndexNextNode = indexNode.next;
if(oldIndexPrevNode == null){
first = oldIndexNextNode;
}else{
oldIndexPrevNode.next = oldIndexNextNode;
indexNode.prev = null;//GC回收
}
if(oldIndexNextNode == null){
last = oldIndexPrevNode;
}else{
oldIndexNextNode.prev = oldIndexPrevNode;
indexNode.next = null;//GC回收
}
indexNode.data = null;//GC回收
size--;
return data;
}
@Override
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
@Override
public boolean remove(Object obj) {
if(obj == null){
//從首節點開始遍歷,找到複合條件的節點
for(Node<E> x = first; x.next !=null; x = x.next){
if(x.data == null){
unlink(x);
return true;
}
}
}else{
for(Node<E> x = first; x.next !=null; x = x.next){
if(obj.equals(x.data)){
unlink(x);
return true;
}
}
}
return false;
}
private static class Node<E> {
E data;//節點資料
Node<E> prev;//上一個節點
Node<E> next;//下一個節點
public Node(E data, Node<E> prev, Node<E> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
}
package com.roger.collection;
public interface RogerList<E> {
boolean add(E e);
boolean add(int index, E e);
E get(int index);
int size();
E remove(int index);
boolean remove(Object obj);
}
package com.roger.collection.impl;
import com.roger.collection.RogerList;
import org.junit.Test;
public class RogerLinkerListTest {
@Test
public void testAdd() {
RogerList<String> rogerArrayList = new RogerLinkedList<>();
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add("Bruce");
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
@Test
public void testAddByPos() {
RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add("Bruce");
rogerArrayList.add(0, "Andy");
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
@Test
public void testRemove(){
RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add("Bruce");
rogerArrayList.add(3, "Andy");
rogerArrayList.remove(3);
rogerArrayList.add("Bruce1");
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
@Test
public void testRemoveByObj(){
RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add(null);
rogerArrayList.add("Bruce");
rogerArrayList.add(3, "Andy");
rogerArrayList.add(null);
rogerArrayList.remove("Andy");
rogerArrayList.remove(null);
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
}