私は常に単純に使用するものでした:
List<String> names = new ArrayList<>();
移植性のタイプ名としてインターフェースを使用しているので、このような質問をしたときにコードを書き直すことができます。
いつLinkedList
、ArrayList
またはその逆に使用する必要がありますか?
私は常に単純に使用するものでした:
List<String> names = new ArrayList<>();
移植性のタイプ名としてインターフェースを使用しているので、このような質問をしたときにコードを書き直すことができます。
いつLinkedList
、ArrayList
またはその逆に使用する必要がありますか?
回答:
概要 ArrayList
ではArrayDeque
で好ましい多くのより多くのユースケースLinkedList
。不明な場合は、から始めてくださいArrayList
。
LinkedList
とArrayList
Listインターフェースの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)で実行できます。配列リストでは、配列の残りの部分を移動(つまり、コピー)する必要があります。反対に、最悪の場合LinkedList
はO(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
は、より高い初期容量でを構築します。
O(n/2)
またはのようなものはありませんO(n/4)
。大きなO表記は、nが大きくなると演算がどのようにスケールするかを示します。ステップを必要とする操作は、ステップを必要とする操作とまったく同じようにスケーリングされます。これが、一定の加数または因子が削除される理由です。そして、どちらもちょうどです。そして、それは比較するSENCEことはないだろうので、とにかく異なる一定の因子を持っていると1の他のを、両方のちょうど表す直線的に拡大縮小の操作を。n/2
n
O(n/2)
O(n/4)
O(n)
LinkedList
ArrayList
O(n/2)
O(n/4)
これまでのところ、a LinkedList
はa よりも「はるかに多い」という一般的なコンセンサスを除いて、これらの各リストのメモリフットプリントに対処した人はいないようArrayList
です。
相対システムでは参照が32ビットまたは64ビット(nullの場合でも)であるため、32ビットLinkedLists
と64ビットの4セットのデータとを含めましたArrayLists
。
注:ArrayList
行に表示されるサイズはトリミングされたリスト用です -実際には、バッキング配列の容量ArrayList
は通常、現在の要素数よりも大きくなります。
注2: (BeeOnRopeに感謝) CompressedOopsはデフォルトでJDK6の中間以降になっているため、64ビットマシンの以下の値は、特にオフにしない限り、基本的に32ビットマシンと一致します。
結果LinkedList
はArrayList
、特にが非常に高い要素数の場合、それがを大きく上回ることを明確に示しています。メモリが重要な要素である場合は、を避けてください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)
int
4バイトまたは8バイトのデータのみを含むオブジェクトをモデリングしています。リンクされたリストには、基本的に4ワードのオーバーヘッドがあります。したがって、グラフは、リンクされたリストが配列リストのストレージの「5倍」を使用するという印象を与えます。これは間違っています。オーバーヘッドは、スケーリング係数ではなく、追加の調整として、オブジェクトごとに16バイトまたは32バイトです。
CompressedOops
64ビットの違いすることはありませんので、(数年のために7、8と6のアップデート)すべて最近のJDKにデフォルトになりましたArrayList
かLinkedList
明示的にするために圧縮おっとをオフにしていない限り、サイズをなんらかの理由。
ArrayList
、初期容量を指定せずにを設定しても、それよりもメモリの使用量は大幅に少なくなりますLinkedList
。
ArrayList
あなたが欲しいものです。LinkedList
ほとんどの場合、(パフォーマンス)バグです。
なぜLinkedList
吸う:
ArrayList
を使用した場合よりも遅くなります。ArrayList
いずれにしてもかなり遅くなる可能性があります。LinkedList
はおそらく間違った選択なので、ソースで見るのは不快です。約10年間、非常に大規模なSOA Webサービスで運用パフォーマンスエンジニアリングを行ってきた人として、ArrayListよりもLinkedListの動作を好むでしょう。LinkedListの定常状態のスループットは低下するため、ハードウェアの追加購入につながる可能性があります-圧力下のArrayListの動作は、クラスター内のアプリがほぼ同期してアレイを拡張することにつながる可能性があり、大きなアレイサイズの場合、応答性の欠如につながる可能性がありますアプリ内と停止中に、圧力下で、壊滅的な動作です。
同様に、デフォルトのスループットテニュアガベージコレクターからアプリのスループットを向上させることができますが、10 GBのヒープを持つJavaアプリを取得すると、フルGC中に25秒間アプリをロックアップして、SOAアプリでタイムアウトと障害を引き起こす可能性があります。頻繁に発生する場合は、SLAを破棄します。CMSコレクターはより多くのリソースを使用し、同じ生のスループットを実現しませんが、予測可能性が高く、レイテンシが小さいため、CMSコレクターの方がはるかに優れた選択肢です。
ArrayListは、パフォーマンスがスループットであり、レイテンシを無視できる場合にのみ、パフォーマンスの優れた選択肢です。私の仕事での経験では、最悪の場合の待ち時間を無視することはできません。
LinkedList
常にので、参照の普通アレイ5倍のメモリを割り当てるArrayList
一時的に2.5倍を必要とすることは、依然としてメモリが再利用されていない間も、はるかに少ないメモリを消費します。大規模な配列の割り当てはEdenスペースをバイパスするため、メモリが十分にない場合を除いて、GCの動作に影響を与えません。その場合、メモリLinkedList
がかなり早く不足します...
LinkedList
次の要素に割り当てるために必要な空きメモリはごくわずかです。サイズを変更した配列を割り当てるにはArrayList
、大きくて連続した空きブロックが必要です。ヒープが断片化すると、GCは、適切な単一のメモリブロックを解放するために、ヒープ全体を並べ替えてしまう可能性があります。
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)
ArrayListsは、追記型またはアペンダーには適していますが、フロントまたはミドルからの追加/削除には適していません。
O(1)
。挿入ポイントを見つけるには、リストの半分を実行する必要があります。
LinkedList
ある O(1)
あなたが挿入位置にイテレータを持っている場合、つまりはListIterator.add
おそらくあるO(1)
ためLinkedList
。
ええ、私は知っています、これは古代の質問ですが、私は私の2セントを投入します:
LinkedListは、ほとんどの場合、パフォーマンスの点で間違った選択です。LinkedListが要求される非常に特殊なアルゴリズムがいくつかありますが、それらは非常にまれであり、アルゴリズムは通常、リストの中央にあるエレメントを比較的迅速に挿入および削除するLinkedListの機能に特に依存します。 ListIteratorを使用します。
LinkedListがArrayListよりも優れている一般的な使用例として、キューの使用例があります。ただし、目標がパフォーマンスである場合は、LinkedListの代わりに、ArrayBlockingQueue(キューサイズの上限を事前に決定でき、すべてのメモリを事前に割り当てる余裕がある場合)、またはこのCircularArrayList実装の使用も検討する必要があります。。(はい、それは2001年からなので、一般化する必要がありますが、最近のJVMで今の記事で引用されているものと同等のパフォーマンス比を得ました)
ArrayDeque
。docs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
ArrayDeque
LinkedList
すべての操作が同じ終了でない場合よりも遅くなります。スタックとして使用する場合は問題ありませんが、適切なキューを作成できません。
ArrayDeque
Stack
スタックとして使用する場合よりも高速でLinkedList
、キューとして使用する場合よりも高速になる可能性があります。
ArrayDeque
ドキュメントからの引用であることに注意してください。
それは効率の問題です。LinkedList
要素の追加と削除は高速ですが、特定の要素へのアクセスは低速です。ArrayList
特定の要素へのアクセスは高速ですが、どちらかの端に追加するのが遅く、特に途中で削除するのが遅くなることがあります。
配列対ArrayList対LinkedList対Vectorは、Linked Listと同様に、より深くなり ます。
正しいか正しくないか:ローカルでテストを実行し、自分で決めてください!
では、編集/削除の方が高速LinkedList
ですArrayList
。
ArrayList
Array
サイズを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);
}
}
LinkedList
すべての要素に対して5つのフィールドを持つノードオブジェクトがあるため、メモリオーバーヘッドははるかに大きくなります。20バイトのオーバーヘッドが発生する多くのシステム。の要素あたりの平均メモリオーバーヘッドArrayList
は1.5ワードで、6バイト、最悪の場合は8バイトになります。
removeIf(element -> condition)
方がはるかに高速ですArrayList
。理論的にはO(1)であるので、これがLinkedList
特定のシナリオに依存するよりもパフォーマンスが良いか悪いかは異なりLinkedList
ますが、単一のノードだけを削除すると、いくつかのメモリアクセスが必要になりArrayList
、かなりの数の要素を削除するときに必要な数を簡単に超える可能性があります。 。
ArrayList
本質的に配列です。LinkedList
二重リンクリストとして実装されます。
get
かなり明確です。インデックスを使用してランダムアクセスを許可するためArrayList
、のO(1)ArrayList
。O(n)はLinkedList
、インデックスを最初に見つける必要があるためです。注:とには異なるバージョンがadd
ありremove
ます。
LinkedList
追加と削除は高速ですが、取得は低速です。簡単に言うと、次のLinkedList
場合に推奨されます。
=== ArrayList ===
=== LinkedList ===
add(E e)
add(intインデックス、E要素)
ここから図であるprogramcreek.com(add
及びremove
リストの末尾に要素を追加し、リスト内の指定された位置にある要素を削除し、すなわち、第一タイプです。)。
LinkedListの作者であるJoshua Bloch:
誰かが実際にLinkedListを使用していますか?私はそれを書いた、そして私はそれを使ったことはない。
リンク:https : //twitter.com/joshbloch/status/583813919019573248
他の回答ほど有益ではない回答については申し訳ありませんが、私はそれが最も興味深く自明であると思いました。
ArrayList
ランダムにアクセスできますが、LinkedList
要素の拡張と削除は非常に安価です。ほとんどの場合、問題ArrayList
ありません。
大きなリストを作成してボトルネックを測定していない限り、違いを心配する必要はおそらくないでしょう。
現代のコンピューターアーキテクチャによるTL; DRはArrayList
、ほぼすべての可能なユースケースで大幅に効率がよくなるためLinkedList
、非常にユニークで極端な場合を除いては避けてください。
理論的には、LinkedListにはO(1)があり、 add(E element)
また、リストの途中に要素を追加すると、非常に効率的です。
LinkedListは悪意のあるデータのキャッシュ構造であるため、実際は非常に異なります。パフォーマンスPOVから- キャッシュフレンドリーLinkedList
よりもパフォーマンスが優れているケースはほとんどありません。 ArrayList
以下は、ランダムな場所に要素を挿入するベンチマークテストの結果です。ご覧のとおり-配列リストの方がはるかに効率的ですが、理論的にはリストの中央に挿入するたびに、配列のn個後の要素を「移動」する必要があります(値が小さいほど優れています)。
後期ハードウェア(より大きく、より効率的なキャッシュ)での作業-結果はさらに決定的です。
LinkedListが同じ作業を完了するには、さらに時間がかかります。ソース ソースコード
これには主に2つの理由があります。
主に -のノードがLinkedList
メモリ全体にランダムに散らばっていること。RAM(「ランダムアクセスメモリ」)は実際にはランダムではなく、メモリのブロックをキャッシュにフェッチする必要があります。この操作には時間がかかり、このようなフェッチが頻繁に発生する場合、キャッシュ内のメモリページを常に交換する必要がある->キャッシュミス->キャッシュは効率的ではありません。
ArrayList
要素は連続メモリに保存されます-これはまさに、最新のCPUアーキテクチャが最適化しているものです。
セカンダリ LinkedList
/バックポインターを保持するために必要です。つまり、と比較して、保存される値ごとのメモリ消費量の3倍になりますArrayList
。
DynamicIntArray、btw、はInt
Objectではなく(プリミティブ型)を保持するカスタム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によって作成されました。彼のブログでさらに多くのデータを見つけることができます
あなたのコードがある場合はadd(0)
とremove(0)
、使用しLinkedList
、それはきれいだaddFirst()
し、removeFirst()
方法。それ以外の場合は、を使用しますArrayList
。
そしてもちろん、GuavaのImmutableListはあなたの親友です。
ArrayList
とLinkedList
と両方の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)
これらに基づいて、何を選択するかを決定する必要があります。:)
LinkedList.add()
ありませんが、ここでの答えのほとんどはそうです。
LinkedListとArrayListをパラメーターの下で比較してみましょう。
ArrayListは、リストインターフェイスのサイズ変更可能な配列実装です。
LinkedListは、リストインターフェイスの二重リンクリストの実装です。
ArrayList get(int index)操作は一定時間、つまりO(1)で実行されますが、
LinkedList get(int index)操作の実行時はO(n)です。
背後にある理由のArrayListが速くLinkedListのよりあるが、それは内部一方、配列を使用するようにArrayListのは、その要素のインデックスベースのシステムを使用していることです
LinkedListは、指定された要素のインデックスでノードを取得するために最初または最後(どちらか近い方)から反復するため、要素のインデックスベースのアクセスを提供しません。
LinkedListへの挿入は、ArrayListに比べて一般的に高速です。LinkedListでは、追加または挿入はO(1)操作です。
しながら、ArrayListのアレイが満杯すなわち最悪の場合であれば、アレイのサイズを変更し、ArrayListのO(N)に追加操作の実行を行う新しい配列に要素のコピーの追加コストは、それ以外の場合はO(1)であり、あります。
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)です。
LinkedListは、descendingIterator()を使用して逆方向に反復できます。
ArrayListにはdescendingIterator()がないため、逆方向にArrayListを反復処理する独自のコードを記述する必要があります。
コンストラクターがオーバーロードされていない場合、ArrayListは初期容量10の空のリストを作成しますが、
LinkedList は、初期容量なしで空のリストのみを作成します。
LinkedListのノードは次のノードと前のノードのアドレスを維持する必要があるため、LinkedListのメモリオーバーヘッドはArrayListに比べて多くなります。ながら
でArrayListの 各インデックスは、実際のオブジェクト(データ)を保持します。
私は通常、その特定のリストで実行する操作の時間の複雑さに基づいて、一方を他方を使用します。
|---------------------|---------------------|--------------------|------------|
| 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 |
|--------------------------------------|
配列リストは基本的に、アイテムなどを追加するためのメソッドを持つ配列です(代わりに汎用リストを使用する必要があります)。これは、インデクサー([0]など)を介してアクセスできるアイテムのコレクションです。これは、あるアイテムから次のアイテムへの進行を意味します。
リンクされたリストは、あるアイテムから次のアイテムへの進行を指定します(アイテムa->アイテムb)。配列リストでも同じ効果が得られますが、リンクリストでは、前のリストの後に続くはずのアイテムが絶対に示されます。
Javaチュートリアル-リスト実装を参照してください。
リンクされたリストの重要な機能(別の回答では読みませんでした)は、2つのリストの連結です。配列では、これはO(n)(+いくつかの再割り当てのオーバーヘッド)であり、リンクされたリストでは、これはO(1)またはO(2)のみです;-)
重要:Javaの場合、LinkedList
これは当てはまりません。参照してくださいJavaでリンクリストの高速concatメソッドはありますか?
next
1つのリストから2番目のリストの最初のノードを指すことはできません。唯一の方法は、addAll()
要素を順番に追加するを使用することですが、ループしてadd()
各要素を呼び出すよりも優れています。O(1)でこれをすばやく行うには、(org.apache.commons.collections.collection.CompositeCollectionなどの)合成クラスが必要ですが、これはあらゆる種類のリスト/コレクションで機能します。
ArrayListとLinkedListには、それぞれ長所と短所があります。
ArrayListは、次のノードへのポインターを使用するLinkedListと比較して、連続したメモリアドレスを使用します。したがって、ArrayListで要素を検索する場合は、LinkedListでn回の反復を実行するよりも高速です。
一方、LinkedListでの挿入と削除は、ポインタを変更するだけなのではるかに簡単ですが、ArrayListは、挿入または削除にシフト操作を使用することを意味します。
アプリで頻繁に取得操作を行う場合は、ArrayListを使用してください。頻繁に挿入と削除を行う場合は、LinkedListを使用してください。
ArrayList
そして、LinkedList
の両方を実装List interface
し、その方法及び結果はほとんど同じです。ただし、要件によっては、両者の違いが少なく、どちらがより優れているかが異なります。
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:
LinkedList
addメソッドは最悪の場合にO(1)
パフォーマンスをArrayList
提供O(n)
します。理由は、削除について説明したものと同じです。
4)要素データと隣接ノードの2つのポインタMemory Overhead:
ArrayList
をLinkedList
維持しながら、インデックスと要素データを維持
そのため、LinkedListではメモリ消費量が比較的多くなります。
iterator
そしてlistIterator
これらのクラスによって返さがありますfail-fast
(リストが構造的に反復子を作成した後以外の方法で、いつでも変更された場合にiterator’s
自身の削除や追加方法、イテレータます)。throw
ConcurrentModificationException
(O(1))
、LinkedList
と比較して優れたパフォーマンスを提供しArrayList(O(n))
ます。
したがって、アプリケーションで頻繁に追加と削除を行う必要がある場合は、LinkedListが最適です。
get method
)操作は高速ですArraylist (O(1))
が、高速ではありませんLinkedList (O(n))
そのため、追加と削除の操作が少なく、検索操作の要件が多い場合は、ArrayListが最適です。
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つは、テストを1回だけ実行します。しかし、私が気付いたのは、これらのテストを何度も実行する必要があり、最終的にはそれらの時間が収束することです。基本的に、JVMはウォームアップする必要があります。私の特定のユースケースでは、約500アイテムに増えるリストにアイテムを追加/削除する必要がありました。私のテストでLinkedList
はLinkedList
、約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;
}
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よりも常に優れていることを私は知っている唯一のパフォーマンス上の利点にします。
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)であります