JavaでArrayListよりもLinkedListを使用する場合


3122

私は常に単純に使用するものでした:

List<String> names = new ArrayList<>();

移植性のタイプ名としてインターフェースを使用しているので、このような質問をしたときにコードを書き直すことができます。

いつLinkedListArrayListまたはその逆に使用する必要がありますか?



1
LinkedList stackoverflow.com/a/42529652/2032701の作者からの引用を参照するだけで、問題の実用的な感覚が得られます。
Ruslan

Java LinkedListとArrayListの投稿を参照してください。この記事では、違いについて説明し、パフォーマンステストも含まれています。
アロナナ

回答:


3371

概要 ArrayListではArrayDequeで好ましい多くのより多くのユースケースLinkedList。不明な場合は、から始めてくださいArrayList


LinkedListArrayListListインターフェースの2つの異なる実装です。LinkedList二重にリンクされたリストでそれを実装します。ArrayList動的にサイズ変更する配列で実装します。

標準のリンクリストおよび配列操作と同様に、さまざまなメソッドには異なるアルゴリズムランタイムがあります。

ために LinkedList<E>

  • get(int index)あるO(N) (有するN / 4平均の段差)が、O(1)の場合index = 0、またはindex = list.size() - 1(ここで、あなたも使用できるgetFirst()getLast())。の主な利点の1つ LinkedList<E>
  • add(int index, E element)されているO(N) (有するN / 4平均の段差)が、O(1)場合index = 0、またはindex = list.size() - 1(この場合には、あなたも使用することができaddFirst()、およびaddLast()/ add())。の主な利点の1つ LinkedList<E>
  • remove(int index)あるO(N) (有するN / 4平均の段差)が、O(1)の場合index = 0、またはindex = list.size() - 1(ここで、あなたも使用できるremoveFirst()removeLast())。の主な利点の1つ LinkedList<E>
  • Iterator.remove()あるO(1) の主な利点の1つ LinkedList<E>
  • ListIterator.add(E element)あるO(1) の主な利点の1つ LinkedList<E>

注:多くの操作には、平均でn / 4ステップ、最良の場合は一定のステップ数(例:インデックス= 0)、最悪の場合はn / 2ステップ(リストの中央)が必要です。

ために ArrayList<E>

  • get(int index)あるO(1) 主な利点 ArrayList<E>
  • add(E element)あるO(1)償却が、O(n)の配列をサイズ変更してコピーしなければならないので、最悪の場合
  • add(int index, E element)あるO(N) (とN / 2平均の手順)
  • remove(int index)あるO(N) (とN / 2平均の手順)
  • Iterator.remove()あるO(N) (とN / 2平均の手順)
  • ListIterator.add(E element)あるO(N) (とN / 2平均の手順)

注:操作の多くは、平均でn / 2ステップ、最良の場合は一定のステップ数(リストの最後)、最悪の場合はnステップ(リストの最初)を必要とします。

LinkedList<E>イテレータを使用した一定時間の挿入または削除が可能ですが、要素への順次アクセスのみが可能です。つまり、リストを前後に移動できますが、リスト内の位置を見つけるには、リストのサイズに比例して時間がかかります。Javadocは、「リストにインデックスを付ける操作は、リストを最初または最後のどちらか近い方からトラバースする」と言っているため、これらのメソッドは平均でO(n)n / 4ステップ)ですが、のO(1)ですindex = 0

ArrayList<E>一方、高速のランダム読み取りアクセスを許可すると、一定の時間で任意の要素を取得できます。しかし、最後以外の場所から追加または削除するには、開口部を作成するか、ギャップを埋めるために、後のすべての要素をシフトする必要があります。また、基になる配列の容量よりも多くの要素を追加すると、新しい配列(サイズの1.5倍)が割り当てられ、古い配列が新しい配列にコピーされるため、最悪の場合ArrayListはis O(n)に追加されます。ケースですが、平均して一定です。

したがって、実行する操作に応じて、それに応じて実装を選択する必要があります。どちらの種類のリストの繰り返し処理も、実質的に同等に安価です。(ArrayList技術的には反復処理の方が高速ですが、パフォーマンスに非常に敏感なことをしているのでない限り、これについて心配する必要はありません。どちらも定数です。)

LinkedList要素を挿入および削除するために既存のイテレータを再利用する場合は、erise を使用する主な利点があります。これらの操作は、リストをローカルでのみ変更することにより、O(1)で実行できます。配列リストでは、配列の残りの部分を移動(つまり、コピー)する必要があります。反対に、最悪の場合LinkedListO(n)n / 2ステップ)でリンクをたどる手段を探しますがArrayList、希望の位置では数学的に計算してO(1)でアクセスできます。

LinkedListこれらの操作はO(1)であり、O(n)であるので、リストの先頭から追加または削除する場合は、rise を使用するもう1つの利点がありますArrayList。注ArrayDequeへの良い代替かもしれLinkedList加えると頭から除去するが、それはないですList

また、大きなリストがある場合は、メモリ使用量も異なることに注意してください。LinkedList次の要素と前の要素へのポインタも格納されるため、aの各要素にはより多くのオーバーヘッドがあります。ArrayListsこのオーバーヘッドはありません。ただし、ArrayLists要素が実際に追加されたかどうかに関係なく、容量に割り当てられているのと同じ量のメモリを使用します。

のデフォルトの初期容量ArrayListはかなり小さい(Java 1.4-1.8から10)。ただし、基本となる実装は配列であるため、多くの要素を追加する場合は、配列のサイズを変更する必要があります。多くの要素を追加することがわかっている場合にサイズ変更の高コストを回避するにArrayListは、より高い初期容量でを構築します。


182
挿入費用について言及するのを忘れていました。LinkedListでは、正しい位置になると挿入にO(1)がかかりますが、ArrayListではO(n)になります-挿入ポイントを過ぎたすべての要素を移動する必要があります。
デビッドロドリゲス-ドリベス2008年

26
Vectorの使用について:Vectorにフォールバックする必要はありません。これを行う方法は、優先するList実装と、それに同期化されたラッパーを提供するためのsynchronizedListへの呼び出しです。参照:java.sun.com/docs/books/tutorial/collections/implementations/…–
Ryan Cox

69
いいえ、LinkedListの場合、位置がわかっていてもgetはO(n)です。その位置に到達するためには、基礎となる実装がリンクリストの「次の」ポインタをたどってその位置の値に到達する必要があるためです。ランダムアクセスなどはありません。位置2の場合、ポインターを歩くのは安上がりかもしれませんが、位置100万の場合はそれほど安くはありません。ポイントは、それは位置に比例すること、つまりO(n)であることです。
ジョナサントラン

53
@Kevinメモリが「近接」していることが重要な場合があります。ハードウェアは、連続したメモリブロック(ダイナミックRAM)をL1またはL2キャッシュの高速スタティックRAMにキャッシュします。理論的には、ほとんどの場合実際上、メモリはランダムアクセスとして扱うことができます。ただし、実際には、メモリを順番に読み取るのは、ランダムな順序よりもわずかに速くなります。パフォーマンスが重要なループの場合、これが問題になる可能性があります。彼らはそれを「空間的局所性」または参照の局所性と呼んでいます
ジョナサントラン

92
O(n/2)またはのようなものはありませんO(n/4)。大きなO表記は、nが大きくなると演算がどのようにスケールするかを示します。ステップを必要とする操作は、ステップを必要とする操作とまったく同じようにスケーリングされます。これが、一定の加数または因子が削除される理由です。そして、どちらもちょうどです。そして、それは比較するSENCEことはないだろうので、とにかく異なる一定の因子を持っていると1の他のを、両方のちょうど表す直線的に拡大縮小の操作を。n/2nO(n/2)O(n/4)O(n)LinkedListArrayListO(n/2)O(n/4)
Holger 2016

630

これまでのところ、a LinkedListはa よりも「はるかに多い」という一般的なコンセンサスを除いて、これらの各リストのメモリフットプリントに対処した人はいないようArrayListです。

相対システムでは参照が32ビットまたは64ビット(nullの場合でも)であるため、32ビットLinkedListsと64ビットの4セットのデータとを含めましたArrayLists

注:ArrayList行に表示されるサイズはトリミングされたリスト用です -実際には、バッキング配列の容量ArrayListは通常、現在の要素数よりも大きくなります。

注2: (BeeOnRopeに感謝) CompressedOopsはデフォルトでJDK6の中間以降になっているため、64ビットマシンの以下の値は、特にオフにしない限り、基本的に32ビットマシンと一致します。


LinkedListとArrayListのグラフ要素数xバイト


結果LinkedListArrayList、特にが非常に高い要素数の場合、それがを大きく上回ることを明確に示しています。メモリが重要な要素である場合は、を避けてくださいLinkedLists

私が使用した式は次のとおりです。何か間違ったことをした場合はお知らせください。修正します。'b'は32または64ビットシステムの場合は4または8であり、 'n'は要素数です。modの理由は、Javaのすべてのオブジェクトがすべて使用されるかどうかに関係なく、8バイトの倍数のスペースを占めるためです。

配列リスト:

ArrayList object header + size integer + modCount integer + array reference + (array oject header + b * n) + MOD(array oject, 8) + MOD(ArrayList object, 8) == 8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8) + MOD(8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8), 8)

LinkedList:

LinkedList object header + size integer + modCount integer + reference to header + reference to footer + (node object overhead + reference to previous element + reference to next element + reference to element) * n) + MOD(node object, 8) * n + MOD(LinkedList object, 8) == 8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n + MOD(8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n, 8)


2
LinkedListが単一の要素を格納するためにArrayListと同じ量のメモリを必要とすることを確認するのは非常に興味深いです。なんて直感的じゃない!-XX:+ UseCompressedOopsを使用して例を実行するとどうなりますか?
jontejj 2013

215
数学の問題は、グラフが影響を非常に誇張することです。それぞれがint4バイトまたは8バイトのデータのみを含むオブジェクトをモデリングしています。リンクされたリストには、基本的に4ワードのオーバーヘッドがあります。したがって、グラフは、リンクされたリストが配列リストのストレージの「5倍」を使用するという印象を与えます。これは間違っています。オーバーヘッドは、スケーリング係数ではなく、追加の調整として、オブジェクトごとに16バイトまたは32バイトです。
ヒースハニカット2013

6
ArrayList / LinkedList / Nodeオブジェクトのどれもintだけを含んでいないので、あなたの言っていることがわかりません。私は明確にするために「オブジェクトオーバーヘッド」を「オブジェクトヘッダー」に言い換えました-システムに関係なくすべてのオブジェクトに8バイトのヘッダーがあり、はい、LinkedListのすべてのNodeオブジェクトが含まれています。言う。ちなみに、もう一度見てみると、LinkedListの計算に他のいくつかの問題があり、実際に除算とArrayListが悪化しています。私はそれを更新し続けてうれしいので、遠慮せずにさらに明確にするために詳細を説明してください。
Numeron 2013年

6
ことに留意すべきであるCompressedOops64ビットの違いすることはありませんので、(数年のために7、8と6のアップデート)すべて最近のJDKにデフォルトになりましたArrayListLinkedList明示的にするために圧縮おっとをオフにしていない限り、サイズをなんらかの理由。
BeeOnRope 2013年

1
@jontejjのデフォルトの容量増加は50%であるためArrayList、初期容量を指定せずにを設定しても、それよりもメモリの使用量は大幅に少なくなりますLinkedList
Holger、

244

ArrayListあなたが欲しいものです。LinkedListほとんどの場合、(パフォーマンス)バグです。

なぜLinkedList吸う:

  • 小さなメモリオブジェクトを大量に使用するため、プロセス全体のパフォーマンスに影響します。
  • 小さなオブジェクトがたくさんあると、キャッシュの局所性が悪くなります。
  • インデックス付きの操作はすべてトラバーサルが必要です。つまり、O(n)のパフォーマンスがあります。これはソースコードでは明らかではないため、アルゴリズムO(n)ArrayListを使用した場合よりも遅くなります。
  • 優れたパフォーマンスを得るには注意が必要です。
  • big-Oのパフォーマンスがと同じである場合でも、ArrayListいずれにしてもかなり遅くなる可能性があります。
  • それLinkedListはおそらく間違った選択なので、ソースで見るのは不快です。

237
ごめんなさい。あなたをダウンとマークしました。LinkedListは最悪です。LinkedListが適切なクラスである場合があります。arraylistよりも優れている状況は多くないことに同意しますが、実際には存在します。ばかげたことをする人を教育しましょう!
デビッドターナー

40
このために多くの反対票を得たのを見て申し訳ありません。実際、JavaのLinkedListを使用する理由はほとんどありません。パフォーマンスの低下に加えて、他の具象リストクラスよりも多くのメモリを使用します(各ノードには2つの追加ポインターがあり、各ノードは個別のラッパーオブジェクトであり、追加のオーバーヘッドバイトが伴います)。
ケビンブロック

42
これは、ここで最も役立つ回答の1つです。多くのプログラマーが(a)抽象データ型と具体的な実装の違い、および(b)パフォーマンスを決定する際の定数要素とメモリオーバーヘッドの実際の重要性を理解できないのは残念です。
Porculus

50
-1:これはかなり点滅したビューです。はい、確かにArrayListは非常に用途の広いツールです。ただし、制限があります。これにより問題が発生する場合があり、LinkedListを使用する必要があります。もちろん、これは非常に特殊なソリューションであり、あらゆる特殊なツールと同様に、ほとんどの場合、汎用的なツールよりも優れています。しかし、それはそれがそれを「吸う」またはそのようなものであることを意味するのではなく、あなたはそれをいつ使うべきかを知る必要があるだけです。
マルコム

27
@DavidTurner:それらは存在しますが、トムのポイントは、質問する必要がある場合はおそらくArrayListが必要だということでした。
user541686 2013年

139

約10年間、非常に大規模なSOA Webサービスで運用パフォーマンスエンジニアリングを行ってきた人として、ArrayListよりもLinkedListの動作を好むでしょう。LinkedListの定常状態のスループットは低下するため、ハードウェアの追加購入につながる可能性があります-圧力下のArrayListの動作は、クラスター内のアプリがほぼ同期してアレイを拡張することにつながる可能性があり、大きなアレイサイズの場合、応答性の欠如につながる可能性がありますアプリ内と停止中に、圧力下で、壊滅的な動作です。

同様に、デフォルトのスループットテニュアガベージコレクターからアプリのスループットを向上させることができますが、10 GBのヒープを持つJavaアプリを取得すると、フルGC中に25秒間アプリをロックアップして、SOAアプリでタイムアウトと障害を引き起こす可能性があります。頻繁に発生する場合は、SLAを破棄します。CMSコレクターはより多くのリソースを使用し、同じ生のスループットを実現しませんが、予測可能性が高く、レイテンシが小さいため、CMSコレクターの方がはるかに優れた選択肢です。

ArrayListは、パフォーマンスがスループットであり、レイテンシを無視できる場合にのみ、パフォーマンスの優れた選択肢です。私の仕事での経験では、最悪の場合の待ち時間を無視することはできません。


8
別の解決策は、ArrayListのEnsureCapacity()メソッドを使用してプログラムでリストのサイズを管理することではないでしょうか?私の質問は、キャッシングまたはdbメカニズムに格納した方がよい場合に、非常に多くのものが壊れやすいデータ構造の束に格納されているのはなぜですか?先日、彼らがArrayListの悪事を上下に誓ったインタビューをしましたが、私はここに来て、複雑さの分析が総合的に優れていることに気づきました!議論のための重要なポイントです。ありがとう!
ingyhere 2012年

22
10GBのヒープを持つJavaアプリを取得したら、フルGC中に25秒間アプリをロックアップしてタイムアウトを引き起こす可能性があります実際にLinkedListを使用すると、フルGC中にガベージコレクターを殺害し、キャッシュミスをオンにして過度に大きなLinkedListを繰り返す必要があります各ノード。
bestss 14

5
それは...恐ろしい解決策です。代わりに、配列リストでEnsureCapacity()を呼び出すだけで済む場合、基本的にはGCのクリーンアップに依存しています。これは非常にコストがかかります...
Philip Devine

5
@Andreas:AはLinkedList 常にので、参照の普通アレイ5倍のメモリを割り当てるArrayList一時的に2.5倍を必要とすることは、依然としてメモリが再利用されていない間も、はるかに少ないメモリを消費します。大規模な配列の割り当てはEdenスペースをバイパスするため、メモリが十分にない場合を除いて、GCの動作に影響を与えません。その場合、メモリLinkedListがかなり早く不足します...
Holger

5
@Andreasもう1つの問題は、メモリの割り当て方法です。LinkedList次の要素に割り当てるために必要な空きメモリはごくわずかです。サイズを変更した配列を割り当てるにはArrayList、大きくて連続した空きブロックが必要です。ヒープが断片化すると、GCは、適切な単一のメモリブロックを解放するために、ヒープ全体を並べ替えてしまう可能性があります。
Piotr Kusmierczyk 2016年

128
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

アルゴリズム:Big-Oh表記

ArrayListsは、追記型またはアペンダーには適していますが、フロントまたはミドルからの追加/削除には適していません。


42
一定の要因を考慮せずにbig-O値を直接比較することはできません。小さなリスト(およびほとんどのリストは小さい)の場合、ArrayListのO(N)はLinkedListのO(1)より高速です。
Porculus

4
小さなリストのパフォーマンスは気にしないし、ループで何らかの形で使用されない限り、コンピューターも気にしない。
Maarten Bodewes、2011

45
LinkedListは実際にはの真ん中に挿入できませんO(1)。挿入ポイントを見つけるには、リストの半分を実行する必要があります。
Thomas Ahle、2011

8
LinkedList:中央のO(1)に挿入-間違っています!LinkedListサイズの1/10の位置への挿入でも、ArrayListの1/10の位置への要素の挿入よりも遅いことがわかりました。そしてさらに悪いことに、コレクションの終わり。ArrayListの最後の位置(最後ではない)への挿入は、LinkedListの最後の位置(最後ではない)への挿入よりも高速です
kachanov

14
@kachanovに挿入すると、LinkedList ある O(1) あなたが挿入位置にイテレータを持っている場合、つまりはListIterator.addおそらくあるO(1)ためLinkedList
QUITあり-Anony-Mousse 2013年

107

ええ、私は知っています、これは古代の質問ですが、私は私の2セントを投入します:

LinkedListは、ほとんどの場合、パフォーマンスの点で間違った選択です。LinkedListが要求される非常に特殊なアルゴリズムがいくつかありますが、それらは非常にまれであり、アルゴリズムは通常、リストの中央にあるエレメントを比較的迅速に挿入および削除するLinkedListの機能に特に依存します。 ListIteratorを使用します。

LinkedListがArrayListよりも優れている一般的な使用例として、キューの使用例があります。ただし、目標がパフォーマンスである場合は、LinkedListの代わりに、ArrayBlockingQueue(キューサイズの上限を事前に決定でき、すべてのメモリを事前に割り当てる余裕がある場合)、またはこのCircularArrayList実装の使用も検討する必要があります。。(はい、それは2001年からなので、一般化する必要がありますが、最近のJVMで今の記事で引用されているものと同等のパフォーマンス比を得ました)


39
Java 6から使用できますArrayDequedocs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
Thomas

1
ArrayDequeLinkedListすべての操作が同じ終了でない場合よりも遅くなります。スタックとして使用する場合は問題ありませんが、適切なキューを作成できません。
ジェレミーリスト

2
Untrue-少なくとも、jdk1.7.0_60および次のテストでのOracleの実装。1000万回ループするテストを作成し、1千万のランダムな整数のDequeがあります。ループ内では、1つの要素からポーリングし、定数要素を提供しています。私のコンピューターでは、LinkedListはArrayDequeよりも10倍以上遅く、使用するメモリも少ないです)。その理由は、ArrayListとは異なり、ArrayDequeは配列の先頭へのポインターを保持するため、先頭が削除されたときにすべての要素を移動する必要がないためです。
Henno Vermeulen

6
ArrayDequeStackスタックとして使用する場合よりも高速でLinkedList、キューとして使用する場合よりも高速になる可能性があります。
akhil_mittal 2015年

3
akhil_mittalのコメントはArrayDequeドキュメントからの引用であることに注意してください。
Stuart Marks

65

それは効率の問題です。LinkedList要素の追加と削除は高速ですが、特定の要素へのアクセスは低速です。ArrayList特定の要素へのアクセスは高速ですが、どちらかの端に追加するのが遅く、特に途中で削除するのが遅くなることがあります。

配列対ArrayList対LinkedList対Vectorは、Linked Listと同様に、より深くなり ます。


54

正しいか正しくないか:ローカルでテストを実行し、自分で決めてください!

では、編集/削除の方が高速LinkedListですArrayList

ArrayListArrayサイズを2倍にする必要があるに裏付けられたは、大容量のアプリケーションではさらに悪くなります。

以下は、各操作の単体テスト結果です。タイミングはナノ秒単位で示されます。


Operation                       ArrayList                      LinkedList  

AddAll   (Insert)               101,16719                      2623,29291 

Add      (Insert-Sequentially)  152,46840                      966,62216

Add      (insert-randomly)      36527                          29193

remove   (Delete)               20,56,9095                     20,45,4904

contains (Search)               186,15,704                     189,64,981

コードは次のとおりです。

import org.junit.Assert;
import org.junit.Test;

import java.util.*;

public class ArrayListVsLinkedList {
    private static final int MAX = 500000;
    String[] strings = maxArray();

    ////////////// ADD ALL ////////////////////////////////////////
    @Test
    public void arrayListAddAll() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        arrayList.addAll(stringList);
        watch.totalTime("Array List addAll() = ");//101,16719 Nanoseconds
    }

    @Test
    public void linkedListAddAll() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);

        watch.start();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);
        watch.totalTime("Linked List addAll() = ");  //2623,29291 Nanoseconds
    }

    //Note: ArrayList is 26 time faster here than LinkedList for addAll()

    ///////////////// INSERT /////////////////////////////////////////////
    @Test
    public void arrayListAdd() {
        Watch watch = new Watch();
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        for (String string : strings)
            arrayList.add(string);
        watch.totalTime("Array List add() = ");//152,46840 Nanoseconds
    }

    @Test
    public void linkedListAdd() {
        Watch watch = new Watch();

        List<String> linkedList = new LinkedList<String>();
        watch.start();
        for (String string : strings)
            linkedList.add(string);
        watch.totalTime("Linked List add() = ");  //966,62216 Nanoseconds
    }

    //Note: ArrayList is 9 times faster than LinkedList for add sequentially

    /////////////////// INSERT IN BETWEEN ///////////////////////////////////////

    @Test
    public void arrayListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX + MAX / 10);
        arrayList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        arrayList.add(insertString0);
        arrayList.add(insertString1);
        arrayList.add(insertString2);
        arrayList.add(insertString3);

        watch.totalTime("Array List add() = ");//36527
    }

    @Test
    public void linkedListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        linkedList.add(insertString0);
        linkedList.add(insertString1);
        linkedList.add(insertString2);
        linkedList.add(insertString3);

        watch.totalTime("Linked List add = ");//29193
    }


    //Note: LinkedList is 3000 nanosecond faster than ArrayList for insert randomly.

    ////////////////// DELETE //////////////////////////////////////////////////////
    @Test
    public void arrayListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.remove(searchString0);
        arrayList.remove(searchString1);
        watch.totalTime("Array List remove() = ");//20,56,9095 Nanoseconds
    }

    @Test
    public void linkedListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.remove(searchString0);
        linkedList.remove(searchString1);
        watch.totalTime("Linked List remove = ");//20,45,4904 Nanoseconds
    }

    //Note: LinkedList is 10 millisecond faster than ArrayList while removing item.

    ///////////////////// SEARCH ///////////////////////////////////////////
    @Test
    public void arrayListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.contains(searchString0);
        arrayList.contains(searchString1);
        watch.totalTime("Array List addAll() time = ");//186,15,704
    }

    @Test
    public void linkedListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.contains(searchString0);
        linkedList.contains(searchString1);
        watch.totalTime("Linked List addAll() time = ");//189,64,981
    }

    //Note: Linked List is 500 Milliseconds faster than ArrayList

    class Watch {
        private long startTime;
        private long endTime;

        public void start() {
            startTime = System.nanoTime();
        }

        private void stop() {
            endTime = System.nanoTime();
        }

        public void totalTime(String s) {
            stop();
            System.out.println(s + (endTime - startTime));
        }
    }


    private String[] maxArray() {
        String[] strings = new String[MAX];
        Boolean result = Boolean.TRUE;
        for (int i = 0; i < MAX; i++) {
            strings[i] = getString(result, i);
            result = !result;
        }
        return strings;
    }

    private String getString(Boolean result, int i) {
        return String.valueOf(result) + i + String.valueOf(!result);
    }
}

1
正確には、ArrayListを2倍にする必要はありません。最初にソースを確認してください。
ダヌビアンセーラー2013

例に欠陥があることに注意してください...次の文字列から削除します:18 + [2、12]バイト( "true0false"、 "true500000false")、平均25バイト、これは要素のサイズです途中で。要素のバイトサイズが大きくなると、リンクリストのパフォーマンスが向上し、リストのサイズが大きくなると、連続する配列(リスト)のパフォーマンスが向上することがわかっています。最も重要なのは、文字列に対して.equals()を実行していることです。これは安価な操作ではありません。代わりに整数を使用した場合、違いがあると思います。
Centril 2014

2
「... 大量のアプリケーションではより悪い」:これは誤解です。LinkedListすべての要素に対して5つのフィールドを持つノードオブジェクトがあるため、メモリオーバーヘッドははるかに大きくなります。20バイトのオーバーヘッドが発生する多くのシステム。の要素あたりの平均メモリオーバーヘッドArrayListは1.5ワードで、6バイト、最悪の場合は8バイトになります。
Lii

1
ここであなたのベンチマークのより良いバージョンを実行し、結果を出しました-addAllが正確に初期サイズのストレージ配列を提供しているため、最初の挿入は常にarraycopy。また、これには、データが収集される前のJITコンパイルを可能にするウォームアップサイクルが含まれます。
BobMcGee 2016年

4
@BillKはJava 8以降で使用できます。個々の要素ごとに残り全体をシフトする必要がないため、イテレータを介したループや削除と比べて、のremoveIf(element -> condition)方がはるかに高速ですArrayList。理論的にはO(1)であるので、これがLinkedList特定のシナリオに依存するよりもパフォーマンスが良いか悪いかは異なりLinkedListますが、単一のノードだけを削除すると、いくつかのメモリアクセスが必要になりArrayList、かなりの数の要素を削除するときに必要な数を簡単に超える可能性があります。 。
ホルガー

50

ArrayList本質的に配列です。LinkedList二重リンクリストとして実装されます。

getかなり明確です。インデックスを使用してランダムアクセスを許可するためArrayList、のO(1)ArrayList。O(n)はLinkedList、インデックスを最初に見つける必要があるためです。注:とには異なるバージョンがaddありremoveます。

LinkedList追加と削除は高速ですが、取得は低速です。簡単に言うと、次のLinkedList場合に推奨されます。

  1. 要素のランダムアクセスは多数ありません
  2. 追加/削除操作が多数あります

=== ArrayList ===

  • add(E e)
    • ArrayListの最後に追加
    • メモリのサイズ変更コストが必要です。
    • O(n)最悪、O(1)は償却済み
  • add(intインデックス、E要素)
    • 特定のインデックス位置に追加する
    • シフトとメモリのサイズ変更に必要なコストが必要
    • オン)
  • remove(intインデックス)
    • 指定された要素を削除する
    • シフトとメモリのサイズ変更に必要なコストが必要
    • オン)
  • remove(オブジェクトo)
    • このリストから指定された要素の最初の出現を削除します
    • 最初に要素を検索し、次にシフトと可能なメモリサイズ変更コストを検索する必要があります
    • オン)

=== LinkedList ===

  • add(E e)

    • リストの最後に追加
    • O(1)
  • add(intインデックス、E要素)

    • 指定した位置に挿入
    • 最初に位置を見つける必要があります
    • オン)
  • 削除する()
    • リストの最初の要素を削除する
    • O(1)
  • remove(intインデックス)
    • 指定されたインデックスを持つ要素を削除する
    • 最初に要素を見つける必要があります
    • オン)
  • remove(オブジェクトo)
    • 指定された要素の最初の出現を削除します
    • 最初に要素を見つける必要があります
    • オン)

ここから図であるprogramcreek.comadd及びremoveリストの末尾に要素を追加し、リスト内の指定された位置にある要素を削除し、すなわち、第一タイプです。)。

ここに画像の説明を入力してください


3
「LinkedListは追加/削除よりも高速です」。間違った、上記の解答をチェックstackoverflow.com/a/7507740/638670を
Nerrve

49

LinkedListの作者であるJoshua Bloch:

誰かが実際にLinkedListを使用していますか?私はそれを書いた、そして私はそれを使ったことはない。

リンク:https : //twitter.com/joshbloch/status/583813919019573248

他の回答ほど有益ではない回答については申し訳ありませんが、私はそれが最も興味深く自明であると思いました。


34

ArrayListランダムにアクセスできますが、LinkedList要素の拡張と削除は非常に安価です。ほとんどの場合、問題ArrayListありません。

大きなリストを作成してボトルネックを測定していない限り、違いを心配する必要はおそらくないでしょう。


15
LinkedListは、要素を追加するのに安価ではありません。ほとんどの場合、100万個の要素をLinkedListに追加するよりもArrayListに追加する方が高速です。また、実際のコードのほとんどのリストは、100万要素もありません。
Porculus

10
いつでも、LinkedListにアイテムを追加するコストがわかります。しないArrayList(通常)。100万のアイテムを含むArrayListに1つのアイテムを追加すると、非常に長い時間かかる可能性があります。事前に割り当てられたスペースがない限り、O(n)操作に加えてストレージが2倍になります。LinkedListへのアイテムの追加はO(1)です。私の最後のステートメントは立っています。
ダスティン

4
1つのアイテムをArrayListに追加することは、100万または10億であってもO(1)です。LinkedListに項目を追加することもO(1)です。「追加」とは、最後に追加することを意味します。
kachanov

あなたは私とは異なる方法で実装を読んだに違いありません。私の経験では、10億の要素配列のコピーは、100万の要素配列のコピーよりも時間がかかります。
ダスティン

6
@kachanovあなたはダスティンを誤解する必要があります。10億個のアイテムの配列を宣言していない限り、最終的に配列のサイズを変更する必要があります。その場合、すべての要素を新しい大きな配列にコピーする必要があるため、O(N)になることがありますが、リンクリストでは常にget O(1)
スタンR.

29

現代のコンピューターアーキテクチャによるTL; DRはArrayList、ほぼすべての可能なユースケースで大幅に効率がよくなるためLinkedList、非常にユニークで極端な場合を除いては避けてください。


理論的には、LinkedListにはO(1)があり、 add(E element)

また、リストの途中に要素を追加すると、非常に効率的です。

LinkedListは悪意のあるデータのキャッシュ構造であるため、実際は非常に異なります。パフォーマンスPOVから- キャッシュフレンドリーLinkedListよりもパフォーマンスが優れているケースはほとんどありません。 ArrayList

以下は、ランダムな場所に要素を挿入するベンチマークテストの結果です。ご覧のとおり-配列リストの方がはるかに効率的ですが、理論的にはリストの中央に挿入するたびに、配列のn個後の要素を「移動」する必要があります(値が小さいほど優れています)。

ここに画像の説明を入力してください

後期ハードウェア(より大きく、より効率的なキャッシュ)での作業-結果はさらに決定的です。

ここに画像の説明を入力してください

LinkedListが同じ作業を完了するには、さらに時間がかかります。ソース ソースコード

これには主に2つの理由があります。

  1. 主に -のノードがLinkedListメモリ全体にランダムに散らばっていること。RAM(「ランダムアクセスメモリ」)は実際にはランダムではなく、メモリのブロックをキャッシュにフェッチする必要があります。この操作には時間がかかり、このようなフェッチが頻繁に発生する場合、キャッシュ内のメモリページを常に交換する必要がある->キャッシュミス->キャッシュは効率的ではありません。 ArrayList要素は連続メモリに保存されます-これはまさに、最新のCPUアーキテクチャが最適化しているものです。

  2. セカンダリ LinkedList /バックポインターを保持するために必要です。つまり、と比較して、保存される値ごとのメモリ消費量の3倍になりますArrayList

DynamicIntArray、btw、はIntObjectではなく(プリミティブ型)を保持するカスタムArrayList実装です。したがって、すべてのデータは実際に隣接して保存されるため、さらに効率的です。

覚えておくべき重要な要素は、メモリブロックをフェッチするコストが、単一のメモリセルにアクセスするコストよりも重要であることです。そのため、リーダーの1MBのシーケンシャルメモリは、さまざまなメモリブロックからこの量のデータを読み取るよりも最大x400倍高速です。

Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

出典:すべてのプログラマーが知っておくべき待ち時間の数

ポイントをより明確にするために、リストの最初に要素を追加するベンチマークを確認してください。これは、理論的にはLinkedListが本当に輝き、ArrayList悪い結果またはさらに悪い結果を示すはずのユースケースです。

ここに画像の説明を入力してください

注:これはC ++ Std libのベンチマークですが、私の以前の経験では、C ++とJavaの結果は非常に似ています。ソースコード

メモリのシーケンシャルバルクのコピーは、最新のCPUによって最適化された操作です-理論を変更し、実際にArrayList/ Vectorはるかに効率的にします


クレジット:ここに掲載されているすべてのベンチマークは、KjellHedströmによって作成されました彼のブログでさらに多くのデータを見つけることができます


私はキューをユニークまたは極端とは呼びません!fifoキューは、ArrayListではなくLinkedListで実装する方がはるかに簡単です。独自の開始、停止、再割り当てを行う必要があるため、実際にはArrayListの悪夢です。配列を使用することもできますが、リンクリストはfifoです。Javaの実装についてはわかりませんが、LinkedListはキュー操作とデキュー操作の両方でO(1)を実行できます(Javaが持っていると思いますが、ダブルチェックしていないので、削除のテール要素への特別なポインターが必要です。)
ビルK

24

あなたのコードがある場合はadd(0)remove(0)、使用しLinkedList、それはきれいだaddFirst()し、removeFirst()方法。それ以外の場合は、を使用しますArrayList

そしてもちろん、GuavaImmutableListはあなたの親友です。


3
小さなリストの場合でも、ArrayList.add(0)は常にLinkedList.addFirst()より高速です。
Porculus

1
@Porculus小さなリストの場合、ArrayList.add(0)の方が高速になるというこの議論を常に聞いています。10要素、1000万、?
garg10may 2016

1
@ garg10may smallは10未満
ジェシーウィルソン

@Porculus小さいとは、ArrayListの下にある内部配列の最大容量よりも小さいことを意味します。
Janac Meena

21

私はこれが古い記事であることを知っていますが、正直に言って、誰もがそのLinkedList実装について言及しているとは信じられませんDequeDeque(およびQueue);のメソッドを見てください。あなたが公正な比較をしたい場合は、実行してみてLinkedList反対ArrayDequeで機能のため、機能の比較を行います。


18

ArrayListLinkedListと両方のBig-O表記はCopyOnWrite-ArrayList次のとおりです。

配列リスト

get                 O(1)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

LinkedList

get                 O(n)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(1)
iterator.remove     O(1)

CopyOnWrite-ArrayList

get                 O(1)
add                 O(n)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

これらに基づいて、何を選択するかを決定する必要があります。:)


9
>>>> ArrayList add-> O(1)<-truではありません。場合によっては、もう1つの要素を追加するためにArrayListを拡張する必要があります
kachanov

1
LinkedListの削除はO(1)ではなく、削除する要素を検索する必要があるため、最悪の場合はO(n)および平均O(n / 2)
garg10may

どちらもそうではLinkedList.add()ありませんが、ここでの答えのほとんどはそうです。
ローンの侯爵

18

LinkedListとArrayListをパラメーターの下で比較してみましょう。

1.実装

ArrayListは、リストインターフェイスのサイズ変更可能な配列実装です。

LinkedListは、リストインターフェイスの二重リンクリストの実装です。


2.パフォーマンス

  • get(int index)または検索操作

    ArrayList get(int index)操作は一定時間、つまりO(1)で実行されますが、

    LinkedList get(int index)操作の実行時はO(n)です。

    背後にある理由のArrayListが速くLinkedListのよりあるが、それは内部一方、配列を使用するようにArrayListのは、その要素のインデックスベースのシステムを使用していることです

    LinkedListは、指定された要素のインデックスでノードを取得するために最初または最後(どちらか近い方)から反復するため、要素のインデックスベースのアクセスを提供しません。

  • insert()またはadd(Object)操作

    LinkedListへの挿入は、ArrayListに比べて一般的に高速です。LinkedListでは、追加または挿入はO(1)操作です。

    しながら、ArrayListのアレイが満杯すなわち最悪の場合であれば、アレイのサイズを変更し、ArrayListのO(N)に追加操作の実行を行う新しい配列に要素のコピーの追加コストは、それ以外の場合はO(1)であり、あります。

  • remove(int)操作

    LinkedListでの削除操作は、通常、ArrayList、つまりO(n)と同じです。

    ではLinkedListは、2つのオーバーロードされた削除の方法があります。1つはパラメーターのないremove()で、リストの先頭を削除し、一定時間O(1)で実行されます。LinkedListの他のオーバーロードされたremoveメソッドは、パラメーターとして渡されたObjectまたはintを削除するremove(int)またはremove(Object)です。このメソッドは、オブジェクトが見つかるまでLinkedListを走査し、元のリストからリンクを解除します。したがって、このメソッドのランタイムはO(n)です。

    でながらArrayListの削除(int)メソッドは、新しい更新された配列に古い配列からの要素をコピーすることを含む従ってそのランタイムはO(N)です。


3.逆反復子

LinkedListは、descendingIterator()を使用して逆方向に反復できます。

ArrayListにはdescendingIterator()がないため、逆方向にArrayListを反復処理する独自のコードを記述する必要があります。


4.初期容量

コンストラクターがオーバーロードされていない場合、ArrayListは初期容量10の空のリストを作成しますが、

LinkedList は、初期容量なしで空のリストのみを作成します。


5.メモリオーバーヘッド

LinkedListのノードは次のノードと前のノードのアドレスを維持する必要があるため、LinkedListのメモリオーバーヘッドはArrayListに比べて多くなります。ながら

ArrayListの 各インデックスは、実際のオブジェクト(データ)を保持します。


ソース


18

私は通常、その特定のリストで実行する操作の時間の複雑さに基づいて、一方を他方を使用します。

|---------------------|---------------------|--------------------|------------|
|      Operation      |     ArrayList       |     LinkedList     |   Winner   |
|---------------------|---------------------|--------------------|------------|
|     get(index)      |       O(1)          |         O(n)       | ArrayList  |
|                     |                     |  n/4 steps in avg  |            |
|---------------------|---------------------|--------------------|------------|
|      add(E)         |       O(1)          |         O(1)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     | O(n) in worst case  |                    |            |
|---------------------|---------------------|--------------------|------------|
|    add(index, E)    |       O(n)          |         O(n)       | LinkedList |
|                     |     n/2 steps       |      n/4 steps     |            |
|                     |---------------------|--------------------|            |
|                     |                     |  O(1) if index = 0 |            |
|---------------------|---------------------|--------------------|------------|
|  remove(index, E)   |       O(n)          |         O(n)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     |     n/2 steps       |      n/4 steps     |            |
|---------------------|---------------------|--------------------|------------|
|  Iterator.remove()  |       O(n)          |         O(1)       | LinkedList |
|  ListIterator.add() |                     |                    |            |
|---------------------|---------------------|--------------------|------------|


|--------------------------------------|-----------------------------------|
|              ArrayList               |            LinkedList             |
|--------------------------------------|-----------------------------------|
|     Allows fast read access          |   Retrieving element takes O(n)   |
|--------------------------------------|-----------------------------------|
|   Adding an element require shifting | o(1) [but traversing takes time]  |
|       all the later elements         |                                   |
|--------------------------------------|-----------------------------------|
|   To add more elements than capacity |
|    new array need to be allocated    |
|--------------------------------------|

挿入/削除の前面/背面はすべてO(1)であるため、ArrayDequeは配列に対して少しバランスをとっています。リンクリストが優先されるのは、トラバース中の追加/削除(イテレータ操作)だけです。
ビルK

14

上記の他の適切な引数に加えて、ArrayListimplementsのRandomAccess間にLinkedListimplements インターフェースに気付くはずQueueです。

そのため、効率と動作の違いによって、どういうわけか彼らはわずかに異なる問題に対処します(メソッドのリストを参照してください)。


10

これは、リストでさらに実行する操作によって異なります。

ArrayListインデックス付きの値にアクセスする方が高速です。オブジェクトを挿入または削除する場合は、はるかに悪くなります。

詳細については、配列とリンクリストの違いについて述べている記事を読んでください。


2
詳細を読むには、コードを記述しないでください。挿入と削除では、ArrayListの実装がLinkedListよりも速いことがわかります。
kachanov

8

配列リストは基本的に、アイテムなどを追加するためのメソッドを持つ配列です(代わりに汎用リストを使用する必要があります)。これは、インデクサー([0]など)を介してアクセスできるアイテムのコレクションです。これは、あるアイテムから次のアイテムへの進行を意味します。

リンクされたリストは、あるアイテムから次のアイテムへの進行を指定します(アイテムa->アイテムb)。配列リストでも同じ効果が得られますが、リンクリストでは、前のリストの後に続くはずのアイテムが絶対に示されます。


8

2
こんにちは@chharvey、リンクのみの回答は6票を獲得しますか?リンクをサポートできるポイントをいくつか追加してください。オラクルがリンクを変更した場合はどうなりますか?

7

リンクされたリストの重要な機能(別の回答では読みませんでした)は、2つのリストの連結です。配列では、これはO(n)(+いくつかの再割り当てのオーバーヘッド)であり、リンクされたリストでは、これはO(1)またはO(2)のみです;-)

重要:Javaの場合、LinkedListこれは当てはまりません。参照してくださいJavaでリンクリストの高速concatメソッドはありますか?


2
どう?これは、リンクリストのデータ構造に当てはまる可能性がありますが、Java LinkListオブジェクトには当てはまりません。next1つのリストから2番目のリストの最初のノードを指すことはできません。唯一の方法は、addAll()要素を順番に追加するを使用することですが、ループしてadd()各要素を呼び出すよりも優れています。O(1)でこれをすばやく行うには、(org.apache.commons.collections.collection.CompositeCollectionなどの)合成クラスが必要ですが、これはあらゆる種類のリスト/コレクションで機能します。
ケビンブロック

そうですね。私はそれに応じて答えを編集しました。しかし、LinkedListのでそれを行うための「方法」のためこの回答を参照してください。stackoverflow.com/questions/2494031/...
Karussell

7

ArrayListとLinkedListには、それぞれ長所と短所があります。

ArrayListは、次のノードへのポインターを使用するLinkedListと比較して、連続したメモリアドレスを使用します。したがって、ArrayListで要素を検索する場合は、LinkedListでn回の反復を実行するよりも高速です。

一方、LinkedListでの挿入と削除は、ポインタを変更するだけなのではるかに簡単ですが、ArrayListは、挿入または削除にシフト操作を使用することを意味します。

アプリで頻繁に取得操作を行う場合は、ArrayListを使用してください。頻繁に挿入と削除を行う場合は、LinkedListを使用してください。


6

私は応答を読みましたが、意見を聞くために共有したいArrayListよりも常にLinkedListを使用する1つのシナリオがあります。

DBから取得したデータのリストを返すメソッドがあるたびに、常にLinkedListを使用します。

私の理論的根拠は、私が得ている結果の数を正確に知ることは不可能であるため、(容量と実際の要素数の違いがあるArrayListのように)メモリが浪費されることはなく、容量を複製します。

ArrayListに関しては、配列の重複をできるだけ最小限に抑えるために、少なくとも初期容量のコンストラクターを常に使用する必要があることに同意します。


5

ArrayListそして、LinkedListの両方を実装List interface し、その方法及び結果はほとんど同じです。ただし、要件によっては、両者の違いが少なく、どちらがより優れているかが異なります。

ArrayList対LinkedList

1)Search: ArrayList検索操作は、LinkedList検索操作に比べてかなり高速です。get(int index)中には、ArrayListパフォーマンス与えO(1)ながらLinkedListパフォーマンスがあるしO(n)

Reason: ArrayListは、配列データ構造を暗黙的に使用するため、要素のインデックスベースのシステムを維持します。これにより、リスト内の要素の検索が高速になります。反対側LinkedListでは、要素を検索するためにすべての要素をたどる必要がある二重リンクリストを実装します。

2)Deletion: LinkedList削除操作はO(1)パフォーマンスをArrayList提供し、可変パフォーマンスを提供します:O(n)最悪の場合(最初の要素を削除しているとき)およびO(1)最良の場合(最後の要素を削除しているとき)。

結論:LinkedList要素の削除は、ArrayListに比べて高速です。

理由:LinkedListの各要素は、リスト内の両方の隣接要素を指す2つのポインター(アドレス)を保持しています。したがって、削除する必要があるのは、削除するノードの2つの隣接ノード(要素)のポインター位置を変更することだけです。ArrayListでは、削除された要素によって作成されたスペースを埋めるために、すべての要素をシフトする必要があります。

3)Inserts Performance: LinkedListaddメソッドは最悪の場合にO(1)パフォーマンスをArrayList提供O(n)します。理由は、削除について説明したものと同じです。

4)要素データと隣接ノードの2つのポインタMemory Overhead: ArrayListLinkedList維持しながら、インデックスと要素データを維持

そのため、LinkedListではメモリ消費量が比較的多くなります。

これらのクラスには、次のようないくつかの類似点があります。

  • ArrayListとLinkedListはどちらもListインターフェースの実装です。
  • どちらも要素の挿入順序を維持します。つまり、ArrayList要素とLinkedList要素を表示している間、結果セットは要素がリストに挿入された順序と同じになります。
  • これらのクラスは両方とも非同期であり、Collections.synchronizedListメソッドを使用して明示的に同期させることができます。
  • iteratorそしてlistIteratorこれらのクラスによって返さがありますfail-fast(リストが構造的に反復子を作成した後以外の方法で、いつでも変更された場合にiterator’s自身の削除や追加方法、イテレータます)。throwConcurrentModificationException

LinkedListを使用する場合とArrayListを使用する場合

  • 上記で説明したように、挿入および削除操作は(O(1))LinkedListと比較して優れたパフォーマンスを提供しArrayList(O(n))ます。

    したがって、アプリケーションで頻繁に追加と削除を行う必要がある場合は、LinkedListが最適です。

  • 検索(get method)操作は高速ですArraylist (O(1))が、高速ではありませんLinkedList (O(n))

    そのため、追加と削除の操作が少なく、検索操作の要件が多い場合は、ArrayListが最適です。


5

操作は、ArrayListの中で(i)を取得するより速くLinkedListの、ためより:
ArrayListを:インタフェースリストのサイズ変更可能な配列の実装
のLinkedList:リストの二重リンクリストの実装とのDequeインタフェース

リストにインデックスを付ける操作は、リストの最初または最後から、指定したインデックスに近い方をトラバースします。


5

1)基礎となるデータ構造

ArrayListとLinkedListの最初の違いは、LinkedListがLinkedListによってサポートされているのに対し、ArrayListはArrayによってサポートされていることです。これにより、パフォーマンスがさらに異なります。

2)LinkedListはDequeを実装します

ArrayListとLinkedListのもう1つの違いは、Listインターフェースとは別に、LinkedListがDequeインターフェースも実装していることです。これにより、add()とpoll()の先入れ先出し操作とその他のいくつかのDeque関数が提供されます。3)ArrayListへの要素の追加ArrayListへの要素の追加は、配列のサイズ変更をトリガーしない場合はO(1)操作であり、その場合はO(log(n))になります。一方、要素をLinkedListはナビゲーションを必要としないため、O(1)操作です。

4)位置から要素を削除する

たとえばremove(index)を呼び出すことによって特定のインデックスから要素を削除するために、ArrayListはそれをO(n)に近づけるコピー操作を実行しますが、LinkedListはその点にトラバースする必要があるため、O(n / 2)にもなります近接に基づいてどちらの方向からもトラバースできるため。

5)ArrayListまたはLinkedListの反復

反復は、LinkedListとArrayListの両方のO(n)操作です。nは要素の数です。

6)位置から要素を取得する

get(index)操作はArrayListではO(1)ですが、LinkedListではO(n / 2)です。そのエントリまでトラバースする必要があるためです。ただし、Big O表記では、定数を無視するため、O(n / 2)は単なるO(n)です。

7)メモリ

LinkedListは、ラッパーオブジェクトであるEntryを使用します。これは、データと次のノードと前の2つのノードを格納するための静的な入れ子クラスであり、ArrayListはデータをArrayに格納するだけです。

したがって、ArrayListの場合、あるArrayから別のArrayにコンテンツをコピーするときにArrayがサイズ変更操作を実行する場合を除いて、メモリ要件はLinkedListよりも少ないようです。

Arrayが十分に大きい場合、その時点で大量のメモリが必要になり、ガベージコレクションがトリガーされ、応答時間が遅くなる可能性があります。

ArrayListとLinkedListの上記のすべての違いから、remove()またはget()よりも頻繁にadd()操作を行う場合を除き、ほとんどすべての場合でArrayListがLinkedListよりも優れているように見えます。

リンクリストは内部的にそれらの位置の参照を保持し、O(1)時間でアクセスできるため、リンクリストをArrayListよりも簡単に変更できます。特に、開始または終了から要素を追加または削除する場合は簡単です。

つまり、要素を追加する位置に到達するためにリンクリストをたどる必要はありません。その場合、追加はO(n)操作になります。たとえば、リンクリストの途中で要素を挿入または削除します。

私の意見では、Javaの実用的な目的のほとんどのためにLinkedListではなくArrayListを使用します。


1
これは、ここでのグループ全体の最も適切な回答だと思います。正確で有益です。最後の行を変更することをお勧めします。最後に、「キュー以外」を追加します。これは、リンクリストにはまったく意味がない非常に重要な構造です。
Bill K

3

ここで見たテストの1つは、テストを1回だけ実行します。しかし、私が気付いたのは、これらのテストを何度も実行する必要があり、最終的にはそれらの時間が収束することです。基本的に、JVMはウォームアップする必要があります。私の特定のユースケースでは、約500アイテムに増えるリストにアイテムを追加/削除する必要がありました。私のテストでLinkedListLinkedList、約50,000 NSとArrayList約90,000 NSでテストが早く出ました。以下のコードを参照してください。

public static void main(String[] args) {
    List<Long> times = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        times.add(doIt());
    }
    System.out.println("avg = " + (times.stream().mapToLong(x -> x).average()));
}

static long doIt() {
    long start = System.nanoTime();
    List<Object> list = new LinkedList<>();
    //uncomment line below to test with ArrayList
    //list = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        list.add(i);
    }

    Iterator it = list.iterator();
    while (it.hasNext()) {
        it.next();
        it.remove();
    }
    long end = System.nanoTime();
    long diff = end - start;
    //uncomment to see the JVM warmup and get faster for the first few iterations
    //System.out.println(diff)
    return diff;
}

2

remove()とinsert()はどちらも、ArrayListsとLinkedListsの両方でO(n)の実行効率を持っています。ただし、線形処理時間の背後にある理由は、2つの非常に異なる理由に由来します。

ArrayListでは、O(1)の要素に到達しますが、実際に何かを削除または挿入すると、O(n)になります。これは、以下のすべての要素を変更する必要があるためです。

LinkedListでは、目的のインデックスに到達するまで最初から開始する必要があるため、実際に目的の要素に到達するにはO(n)が必要です。remove()の1つの参照とinsert()の2つの参照のみを変更する必要があるため、実際には削除または挿入は一定です。

2つのうちどちらが挿入と削除の方が速いかは、それがどこで発生するかによって異なります。最初に近づくと、比較的少数の要素を経由する必要があるため、LinkedListはより高速になります。終わりに近づくと、ArrayListの方が高速になります。これは、一定の時間内に到達し、それに続く残りのいくつかの要素を変更するだけでよいためです。正確に中央で行われると、n個の要素を通過する方がn個の値を移動するよりも速いため、LinkedListはより高速になります。

おまけ:これら2つのメソッドをArrayListに対してO(1)にする方法はありませんが、実際にはLinkedListsでこれを行う方法があります。途中で要素全体を削除および挿入するリスト全体を調べたいとしましょう。通常、LinkedListを使用して各要素の最初から開始します。イテレーターで作業している現在の要素を「保存」することもできます。イテレーターの助けを借りて、LinkedListで作業するときに、remove()およびinsert()のO(1)効率を取得します。LinkedListがArrayListよりも常に優れていることを私は知っている唯一のパフォーマンス上の利点にします。


1

ArrayListはAbstractListを拡張し、Listインターフェースを実装します。ArrayListは動的配列です。
基本的には、配列

の欠点を克服するために作成されたと言えます。LinkedListクラスは、AbstractSequentialListを拡張し、List、Deque、およびQueueインターフェースを実装します。
パフォーマンス
arraylist.get()はO(1)linkedlist.get()ですが、O(n)
arraylist.add()はO(1)でlinkedlist.add()あり、0(1)
arraylist.contains()はO(n)でlinkedlist.contains()あり、O(n)
arraylist.next()はO(1)でlinkedlist.next()あり、O(1)
arraylist.remove()はO(n)です一方、linkedlist.remove()O(1)である
ArrayListのでは
iterator.remove()O(N)である
InのLinkedListのは、一方
iterator.remove()O(1)であります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.