この記事は、Javaテクノロジ・スタック建築家のマイクロチャネル公共番号に再現されます
OK、今日の記事を開始します。
A、LinkedListのノウハウ
1、リンクリストLinkedListのノウハウ
私たちは、リストは、我々はA泥棒で、警察の結果ではない発見すること、泥棒を捕まえるために、今、警察に、泥棒を捕まえると言って警察の話を見ていると言われる画像の一例であるかを知りたいが、AでそれはD、E、F、などに行ってきました後、関係者は、泥棒がBであってもよいと述べ、警察の結果はBに行き、発見はCでの可能性を知らされていなかった、Cは警察に行って、知性が取得できませんでした。最後に泥棒を捕まえ、実際には、我々は逮捕のプロセス全体が手掛かりチェーンとして見ることができます置く、またはリンクされたリストと呼ばれます。私たちは、各ノードが2つのメッセージを含めるために、我々はノードのリンクリストとして見ることができるすべての場所を見ることができます:一つは地元の情報であり、情報は次の場所です。
少し直感的に示すために、図を用いて、以下の:
ここでは、上記のリストの分類を行うことができます。
-
単独リンクリスト:我々は次である上記のみに対応しています。
-
二重リンクリストは:次のメモリアドレスでも最後でだけでなく、保存されていることを示します。
-
-
循環リスト:円形チェーンで構成されるアドレス全体、すなわち。
-
順序付きリスト:特定の標準、ソートするリストの要素は、ハッシュ値などを比較し、そのような大きさの内容を比較します
我々はすでに実装だけでなく、リストの分類を見てきました、もちろん、リストのいくつかの基本的な特性があり、私たちは知っておく必要があります。
-
アレイでは、要素は、インデックスを介してアクセスされます。しかし、リストはチェーンによる検索だけではありません。これはすぐに要素のリストを見つけることができませんことの利点を示しています。
-
リストには、二つの鎖について、指定された要素の影響まであるので利点は、指定された削除/挿入要素のリストであります
-
ここであっによる時間の変化、そうでない場合は、右側の第1の記憶ノードにトリック、鎖の右側は、その要素ノードの右にそれを見つけることができない、時間のポインタノードを変更するように、次の接合の前に開催されますポイント。
-
また、それは、父ノード(ノード)の単一のリストを見つけることができない、それは多くの場合、一時的に前のノードに保存されます。
2、ご理解のLinkedList
私たちのLinkedListのは、達成するために二重にリンクされたリストです。それは達成するためのリンクリストであるので、その基本的な機能のリストがあるでしょう。それは二重にリンクされたリストを使用して実装されているためと、その焦点は、まだ二重リンクリストの特性であります
-
何の容量制限を一覧表示しませんが、二重リンクリスト自体は、より多くのスペースを使用するだけでなく、追加のリストポインタの操作が必要です。
-
Listインタフェースを実装することに加えて、LinkedListのは、リストの先頭のために、エンドGET、削除、挿入要素で均一な命名規則を提供します。これらの操作は、スタックとしてリストをリンクさせることができる、およびキュー両端キューを使用します。
LinkedListのを見るために相続から3、
これを行うために、私たちはどのような位置の中にJavaコレクションフレームワーク全体の内部のLinkedListのを知っている必要があります。説明するための図:
私たちは、LinkedListののほとんどのルートは、Collectionインタフェースを実装していること。見つける上から のは、LinkedListのからの継承の観点の開始点として、他のコレクションの影響を取り除くしてみましょう:
次のように私たちは、上記の、LinkedListのクラスの継承とインタフェースの実装から見ることができます:
コレクションインタフェース、リストインターフェース、Cloneableをインターフェース、直列化インターフェイス、のDequeインタフェース(インタフェース5)
AbstractCollectionクラス、AbstractListクラス、AbstractSequentialListクラス(クラス3)。
これは、挿入、削除要素の両端支持線形のDequeコレクションを定義します。
第二に、分析のLinkedList
上記リストから継承し、私はあなたがのLinkedListの基本的な理解を持っていなければならないと考えています。ただし、以下の分析では、最も重要な部分です。実際には、LinkedListのCRUD操作はCRUD操作のリストの最下部に基づいており、私たちは覚えておくことが、その後、自分自身のLinkedListを書くために比較することができます
1、Api操作分析
(1)节点Node结构
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; } }
从上面的node定义,我们就可以看到这是一个双向的链表。
(2)构造方法
public LinkedList() {} //调用 addAll(c) 表示将集合c所有元素插入链表中 public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
(3)增加操作
首先是插入单个节点:
//在尾部插入一个节点:add public boolean add(E e) { linkLast(e); return true; } //生成新节点 并插入到链表尾部,更新last/first 节点。 void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null);//以原尾部节点为新节点的前置节点 last = newNode;//更新尾部节点 if (l == null)//若原链表为空链表,需要额外更新头结点 first = newNode; else l.next = newNode; size++; modCount++;//修改modCount } //在指定下标,index处,插入一个节点 public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } //在succ节点前,插入一个新节点e void linkBefore(E e, Node<E> succ) { final Node<E> pred = succ.prev; //以前置和后置节点和元素值e 构建一个新节点 final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null)//如果之前的前置节点是空,说明succ是原头结点。所以新节点是现在的头结点 first = newNode; else pred.next = newNode; size++; modCount++;//修改modCount }
增加操作一定会更改modCount,这里面涉及到了fail-fast机制。可以翻看我之前的文章。
(4)删除操作
//remove目标节点 public E remove(int index) { checkElementIndex(index);//这里未给出,主要是检查下表是否越界 return unlink(node(index));//从链表上删除某节点 } //从链表上删除x节点 E unlink(Node<E> x) { final E element = x.item; //当前节点的元素值 final Node<E> next = x.next; final 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++; //修改modCount return element; //返回取出的元素值 }
(5)更改操作
public E set(int index, E element) { checkElementIndex(index); //检查越界[0,size) Node<E> x = node(index);//取出对应的Node E oldVal = x.item;//保存旧值 供返回 x.item = element;//用新值覆盖旧值 return oldVal;//返回旧值 }
(6)查操作
public E get(int index) { checkElementIndex(index);//判断是否越界 [0,size) return node(index).item; //调用node()方法 取出 Node节点, }
(7)其他操作
public Object[] toArray() { //new 一个新数组 然后遍历链表,将每个元素存在数组里,返回 Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
2、遍历LinkedList
(1)一般的for循环(随机访问)
int size = list.size(); for (int i=0; i<size; i++) { list.get(i); }
(2)for--each循环
for (Integer integ:list)
(3)迭代器iterator
for(Iterator iter = list.iterator(); iter.hasNext();)
iter.next();
(4)用pollFirst()来遍历LinkedList
while(list.pollFirst() != null)
(5)用pollLast()来遍历LinkedList
while(list.pollLast() != null)
(6)用removeFirst()来遍历LinkedList
try { while(list.removeFirst() != null) } catch (NoSuchElementException e) { }
(7)用removeLast()来遍历LinkedList
try { while(list.removeLast() != null) } catch (NoSuchElementException e) { }
注意:在链表结构实现的数据集合中,最好采用Iterator或者foreach的方式遍历,效率最高。
小结:
(1)底层实现:LinkedList的实现是基于双向链表的,且头结点中不存放数据。
(2)构造方法:无参构造方法直接建立一个仅包含head节点的空链表;包含Collection的构造方法,先调用无参构造方法建立一个空链表,而后将Collection中的数据加入到链表的尾部后面。
(3)查找删除:源码中都划分为该元素为null和不为null两种情况来处理,LinkedList中允许元素为null。
(4)LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容的方法。
(5)LinkedList是基于链表实现的,因此插入删除效率高,查找效率低(虽然有一个加速动作)。
(6)注意源码中还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用。
三、linkedList与ArrayList对比分析
下面将对ArrayList与LinkedList进行对比,主要从以下方面进行
相同点
1.接口实现:都实现了List接口,都是线性列表的实现
2.线程安全:都是线程不安全的,都是基于fail-fast机制
不同点
1.底层:ArrayList内部是数组实现,而LinkedList内部实现是双向链表结构
2.接口:ArrayList实现了RandomAccess可以支持随机元素访问,而LinkedList实现了Deque可以当做队列使用
3.性能:新增、删除元素时ArrayList需要使用到拷贝原数组,而LinkedList只需移动指针,查找元素 ArrayList支持随机元素访问,而LinkedList只能一个节点的去遍历