1.データ構造
LinkedListの基礎となるデータ構造は、二重にリンクされたリストです。全体的な構造を上の図に示します。リンクされたリストの各ノードは、前方または後方にトレースできます。
リンクリスト内の各ノードはノードと呼ばれます。ノードには、前のノードと次のノードの位置をそれぞれ表すprev属性とnext属性があります。
最初は二重リンクリストのヘッドノードであり、その前のノードはnullであり、最後は二重リンクリストのテールノードであり、その次のノードもnullです。
リンクリストにデータがない場合、最初と最後は同じノードであり、前と後ろのポイントは両方ともnullです。
二重にリンクされたリストであるため、マシンのメモリが十分に強力である限り、サイズの制限はありません。
ソースコード
private static class Node<E> {
//节点值
E item;
//当前节点的下一个节点
Node<E> next;
//当前节点的前一个节点
Node<E> prev;
//初始化参数顺序分别是前一个节点、本身节点和后一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
ソースコード分析
- ノードノードには、前のノード、それ自体のノード、および次のノードの3つの属性が含まれます。
- ノードノード初期化パラメータシーケンスは、前のノード、それ自体のノード、および次のノードです。
2.追加
新しいノードを追加するときに、リンクリストの先頭に追加するか、リンクリストの末尾に追加するかを選択できます。addメソッドはデフォルトで末尾から追加し、addFirstメソッドは先頭から追加します。具体的なソースコードは次のとおりです。
ソースコード
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//从尾部开始追加节点
void linkLast(E e) {
//尾节点赋值给临时变量
final Node<E> l = last;
//新建节点,l是新节点的前一个节点,e表示当前新节点,null表示当前新节点的后一个节点
final Node<E> newNode = new Node<>(l, e, null);
//将新节点追加到尾部
last = newNode;
//如果链表为空(l和尾节点相等,尾节点为空,链表即为空),头部和尾部是同一个节点且都是新建的节点
if (l == null)
first = newNode;
//否则把前一个节点的下一个节点指向当前节点
else
l.next = newNode;
//修改链表大小和版本
size++;
modCount++;
}
//从头部追加
private void linkFirst(E e) {
//头节点赋值给临时变量
final Node<E> f = first;
//新建节点,null表示前一个节点,e表示当前新节点,f表示新节点的下一个节点,目前值是头节点的值
final Node<E> newNode = new Node<>(null, e, f);
//将新节点追加到头部
first = newNode;
//如果链表为空(f和头节点相等,头节点为空,链表即为空),头部和尾部是同一个节点且都是新建的节点
if (f == null)
last = newNode;
//否则把下一个节点的前一节点指向当前尾节点
else
f.prev = newNode;
//修改链表大小和版本
size++;
modCount++;
}
}
ソースコードの分析ソースコード
から、ヘッドとテールにノードを追加する方法は比較的簡単で、ポインティング位置を変更するだけであることがわかりますが、前者は移動ヘッドノードの前のポイントであり、後者は移動テールノードの次のポイントです。
3.クエリ
リンクリスト内の特定のノードのクエリは比較的遅く、1つずつ検索する必要があります。具体的なソースコードは次のとおりです。
ソースコード
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//根据链表索引位置查询节点
Node<E> node(int index) {
//如果index处于队列的前半部分,从头开始找,size >> 1是size除以2的意思
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//如果index处于队列的后半部分,从尾部开始找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
}
ソースコードの分析ソースコード
から、LinkedListは最初から最後までループを使用せず、単純な二分法を使用していることがわかります。まず、インデックスがリンクリストの前半にあるか後半にあるかを確認します。前半の場合は最初から始め、その逆も同様です。このようにして、サイクル数が少なくとも半分に減り、検索効率が向上します。このアイデアは学ぶ価値があります。
4.
Iteratorインターフェイスは、双方向の反復アクセスを実現するためのLinkedListのニーズを満たすことができないため(Iteratorは最初から最後までのアクセスのみをサポートします)、Javaは、順方向および逆方向の反復メソッドを提供する反復インターフェイスListIteratorを追加しました。 hasPrevious、previous、previousIndex、hasNext、next、nextIndexなど。hasNext、next、removeのソースコードは次のとおりです。
ソースコード
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//判断是否有下一个元素
public boolean hasNext() {
//下一个节点的索引小于链表的大小,就存在下一个元素
return nextIndex < size;
}
//取下一个元素
public E next() {
//检查期望版本号是否发生变化
checkForComodification();
//再次检查
if (!hasNext())
throw new NoSuchElementException();
//next是当前节点,在上一次执行next()方法时被赋值
lastReturned = next;
//next是下一个节点了,为下次迭代做准备
next = next.next;
nextIndex++;
return lastReturned.item;
}
public void remove() {
checkForComodification();
//lastReturned是本次迭代需要删除的值,分空和非空两种情况
//lastReturned为空,说明调用者没有主动执行过next()或者previos(),直接报错
//lastReturned不为空,是在上次执行next()或者previos()方法时赋的值
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
//删除当前节点
unlink(lastReturned);
/**
* next == lastReturned的场景分析
* 从尾到头递归执行,第一次迭代并且要删除最后一个元素的情况下,previous()方法里面设置了lastReturned = next = last,所以next和lastReturned会相等
*/
if (next == lastReturned)
//这时候lastReturned是尾节点,lastNext是null,所以next也是null,这样在previous()执行时,发现next是null,就会把尾节点赋值给next
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
}
5.削除
ノードを削除する方法は、追加と同様です。先頭から削除するか、末尾から削除するかを選択できます。削除操作では、ノードの値、前後のポインティングノードがnullに設定され、最後にGCがそれをリサイクルします。
ソースコード
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//头部删除
//f是链表头节点
private E unlinkFirst(Node<E> f) {
//取出头节点的值作为方法的返回值
final E element = f.item;
//取出头节点的下一个节点
final Node<E> next = f.next;
//帮助GC回收头节点
f.item = null;
f.next = null;
//将头节点的下一个节点设置为头节点
first = next;
//如果next为空,表明链表为空
if (next == null)
last = null;
//链表不为空,将头节点的前一个节点指向null
else
next.prev = null;
//修改链表大小和版本
size--;
modCount++;
return element;
}
}
ソースコード分析
- リンクリスト構造のノード削除操作は比較的簡単で、フロントノードとバックノードのポイントを変更するだけであることがソースコードからわかります。したがって、LinkedListの追加と削除の速度は比較的高速です。
- テール削除ノードコードは、ヘッダー削除ノードコードに似ています。