我們先把LinkedList的增刪改查源碼都閱讀一遍,然後再各個方面進行比較兩者之間的區別。
LinkedList linkedList = new LinkedList();
// add
linkedList.add("1");
// get
linkedList.get(0);
// set
linkedList.set(0, "1");
// remove
linkedList.remove("1");
ArrayList是對象數組,LinkedList是鏈表
先從新增開始,他的步驟也十分的簡單,和ArrayList完全不同的是,一看他的數據結構,屬性中包含next多半是鏈表的結構,但是具體是什麼鏈表,我們還要繼續看他的其他方法。
/**
* Links e as last element.
*/
void linkLast(E e) {
// 最後一個元素賦值給l
final LinkedList.Node<E> l = last;
// 新建一個節點賦值給newNode
// 下一個節點爲空
// item爲設置的節點
// 最後一個節點賦值給上一個節點
final LinkedList.Node<E> newNode = new LinkedList.Node<>(l, e, null);
// 當前新建的節點賦值爲最後一個節
last = newNode;
// 最後一個節點爲空
// 也就是第一個節點被設置的時候
if (l == null)
// 設置最新的節點爲第一個節點
first = newNode;
else
// 新增前的最後一個節點的next屬性爲當前新增節點
l.next = newNode;
// 長度自增
size++;
modCount++;
}
LinkedList是雙向鏈表
我們再看看他內部的Node對象有哪些屬性。
Node(LinkedList.Node<E> prev, E element, LinkedList.Node<E> next) {
// 當前節點
this.item = element;
// 下一個節點
this.next = next;
// 上一個節點
this.prev = prev;
}
然後繼續看他的取數邏輯是怎樣的,一會兒從前面第一個節點開始,一會兒從最後一個節點開始取數。
/**
* Returns the (non-null) Node at the specified element index.
*/
LinkedList.Node<E> node(int index) {
// assert isElementIndex(index);
// 下標 小於 長度除以2
if (index < (size >> 1)) {
// 第一個節點賦值給x
LinkedList.Node<E> x = first;
// 正向遍歷到下標節點爲止
for (int i = 0; i < index; i++)
// x的下一個元素賦值給x
x = x.next;
// 返回到下標爲止的x對象
return x;
} else {// 下標 大於等於 長度除以2
// 最後一個節點賦值給x
LinkedList.Node<E> x = last;
// 反向遍歷對象
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
一開始我對這個邏輯很費解,但是後來思考了一下,他是一個鏈表的結構,並且存儲了第一個節點和最後一個節點作爲遍歷的開始節點,也就是雙鏈表的結構。這裏的index < (size >> 1)是爲了判斷從哪裏開始遍歷這個鏈表才能讓,也就是我們常用的二分法,從而降低複雜度從而加快查詢的速度。
再看看改的邏輯是什麼,基本和取數的邏輯相似,只是做了item屬性的替換而已。
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
checkElementIndex(index);
// 和get邏輯類似
Node<E> x = node(index);
E oldVal = x.item;
// 匹配到就修改他內部的item屬性
x.item = element;
return oldVal;
}
刪除的邏輯我最後定位都了unlink方法。
/**
* Unlinks non-null node x.
*/
E unlink(LinkedList.Node<E> x) {
// assert x != null;
// x的值賦值個體element
final E element = x.item;
// x的下一個節點賦值給next
final LinkedList.Node<E> next = x.next;
// x的上一個節點賦值給prev
final LinkedList.Node<E> prev = x.prev;
// 上一個節點爲空
// 也就是當前節點是第一個節點
if (prev == null) {
// 第一個節點爲下一個節點
first = next;
} else {
// 下一個節點賦值給上一個節點的下一個節點
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
這個也很好理解,我們畫個圖理解一下。
他和ArrayList相同點是,他們都不是真正意義上的刪除,被“刪除”的空間還是被繼續佔用,前者是前後節點通知內容的替換,而後者則是本身數組的替換。
參考資料
總結
- LinkedList是鏈表結構,ArrayList是對象數組。
- LinkedList是雙鏈表結構。
- LinkedList和ArrayLsit都不是實際意義上的刪除。
文章中出現的任何錯誤歡迎指正,共同進步!
最後做個小小廣告,有對WEB開發和網絡安全感興趣的,可以加羣一起學習和交流!
QQ:425343603