web-dev-qa-db-ja.com

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

私はいつも単純に使うことをずっとしてきました:

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

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

LinkedListArrayList の上でいつ使用すべきか、またその逆の場合

2826
sdellysse

概要ArrayListname__とArrayDequename__は、manyでは、LinkedListname__よりも多くのユースケースに適しています。よくわからない場合は、ArrayListname__で始めてください。


LinkedListname__とArrayListname__は、Listインターフェイスの2つの異なる実装です。 LinkedListname__は、二重にリンクされたリストで実装します。 ArrayListname__は、動的にサイズ変更する配列で実装します。

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

LinkedList<E> の場合

  • get(int index)O(n)(平均でn/4ステップ)
  • add(E element)O(1)
  • add(int index, E element)O(n)(平均でn/4ステップ)ですが、O(1) when index = 0 <- -LinkedList<E>の主な利点
  • remove(int index)O(n)(平均でn/4ステップ)
  • Iterator.remove()O(1)です。 <--- LinkedList<E>の主な利点
  • ListIterator.add(E element)O(1)これはLinkedList<E>の主な利点の1つです

注:操作の多くは、平均で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>は、一定時間の挿入または削除を許可しますsing iteratorsですが、要素の順次アクセスのみです。つまり、リストを前後に移動できますが、リスト内の位置を見つけるには、リストのサイズに比例した時間がかかります。 Javadocは、"リストにインデックスを付ける操作は、リストの先頭または末尾のどちらか近い方を走査します"であるため、これらのメソッドはO(n)(- n/4ステップ)、平均してO(1)index = 0の場合。

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

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

LinkedListname__を使用する主な利点は、既存のイテレーターを再利用して要素を挿入および削除するときに発生します。これらの操作は、リストをローカルでのみ変更することでO(1)で実行できます。配列リストでは、配列の残りの部分をmoved(つまりコピー)にする必要があります。一方、LinkedListname__でシークすると、最悪の場合はO(n)n/2ステップ)のリンクをたどることになりますが、ArrayListname__では数学的に計算され、O(1)でアクセスされます。

リストの先頭に追加または削除するときにLinkedListname__を使用するもう1つの利点は、これらの操作がO(1)である一方で、O(n)であるためです。 ArrayListname__。 ArrayDequename__は、LinkedListname__に代わる適切な代替手段である可能性があることに注意してください。ただし、Listname__ではありません。

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

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

3177
Jonathan Tran

これまでのところ、LinkedListArrayListより「もっとたくさん」であるという一般的なコンセンサスを除いて、これらのリストそれぞれのメモリ使用量に誰も対処していないようです。 。

相対システムでは、参照は32ビットまたは64ビット(nullの場合でも)なので、32ビットおよび64ビットのLinkedListsおよびArrayListsに4つのデータセットを含めました。

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

注2: (thanks BeeOnRope) 現在、JDK 6以降のバージョンではCompressedOopsがデフォルトになっているため、64ビットマシンの場合、下の値は基本的に32ビット版と同じです。消して。


Graph of LinkedList and ArrayList No. of Elements x Bytes


その結果は、特に非常に高い要素数の場合、LinkedListArrayListよりもはるかに多いことを明確に示しています。記憶が要因であるならば、LinkedListsを避けてください。

私が使った式は、私が間違ったことをしたかどうか私に教えてください、そして私はそれを直します。 32または64ビットシステムの場合、 'b'は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)

567
Numeron

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

なぜLinkedListが吸うのか:

  • 小さなメモリオブジェクトを多数使用するため、プロセス全体のパフォーマンスに影響があります。
  • たくさんの小さなオブジェクトはキャッシュの局所性には良くありません。
  • どのインデックス付き演算もトラバースを必要とし、すなわち、O(n)性能を有する。これはソースコードでは明白ではなく、ArrayListが使われた場合よりもアルゴリズムO(n)が遅くなります。
  • 良いパフォーマンスを得るのは難しいです。
  • Big-OパフォーマンスがArrayListと同じであっても、とにかくかなり遅くなるでしょう。
  • それはおそらく間違った選択であるため、ソースのLinkedListを見るのは不快です。
217

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

同様に、アプリのスループットはデフォルトのスループットガベージコレクターから向上させることができますが、10GBのヒープを持つJavaアプリを取得すると、フルGCの間にアプリを25秒間ロックアップすることができます。 SOAアプリのタイムアウトと失敗、およびSLAが頻繁に発生する場合の打撃。 CMSコレクターはより多くのリソースを使用し、同じ生のスループットを達成していませんが、より予測可能で待ち時間が短いため、より良い選択です。

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

137
lamont
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は、追記型やアペンダには適していますが、前面または中央からの追加/削除はできません。

117
Michael Munsey

ええ、これは古代の質問ですが、2セントを投入します。

LinkedListはほぼ常にパフォーマンス的に間違った選択です。 LinkedListが要求される非常に特定のアルゴリズムがいくつかありますが、それらは非常にまれであり、アルゴリズムは通常、リスト内の要素を比較的すばやく挿入および削除するLinkedListの機能に依存します。 ListIteratorを使用します。

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

103
Daniel Martin

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

Array vs ArrayList vs LinkedList vs Vectorリンクリスト .

55
dgtized

正解または不正解:ローカルでtestを実行して自分で決めてください。

編集/削除は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);
    }
}
52
Ash

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

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

LinkedListは追加と削除のほうが速くなりますが、取得は遅くなります。簡単に言うと、LinkedListは次のような場合に推奨されます。 

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

=== ArrayList ===

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

=== LinkedList ===

  • 追加(E e)

    • リストの末尾に追加する
    • O(1)
  • add(int index、E要素)

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

これは programcreek.com からの図です(addremoveは最初の型です。つまり、リストの最後に要素を追加し、リストの指定された位置にある要素を削除します)。

enter image description here

44
Ryan

1) 検索: ArrayList検索操作は、LinkedList検索操作と比較してかなり高速です。 ArrayListのget(int index)は、O(1)のパフォーマンスを示します。一方、LinkedListのパフォーマンスはO(n)です。

理由: ArrayListは、暗黙的に配列データ構造を使用するため、要素のインデックスベースのシステムを維持します。これにより、リスト内の要素の検索が高速になります。一方、LinkedListは、要素を検索するためにすべての要素を横断することを要求する二重リンクリストを実装しています。

2) 削除: LinkedListの削除操作でO(1)のパフォーマンスが得られ、ArrayListでは可変のパフォーマンスが得られます。最悪の場合O(n)そして最善の場合(最後の要素を削除している間)はO(1)です。

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

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

3) パフォーマンスの挿入: LinkedListのaddメソッドはO(1)のパフォーマンスを示し、ArrayListは最悪の場合O(n)を示します。その理由はremoveの説明と同じです。

4) メモリオーバーヘッド: ArrayListはインデックスと要素データを保持し、LinkedListは要素データと隣接ノードの2つのポインタを保持するため、LinkedListではメモリ消費量が比較的多くなります。

これらのクラスの間には いくつかの類似点 があります。

ArrayListとLinkedListはどちらもListインタフェースの実装です。これらはどちらも要素の挿入順序を維持します。つまり、ArrayListとLinkedList要素を表示している間、結果セットは要素がListに挿入された順序と同じ順序になります。これらのクラスはどちらも非同期であり、Collections.synchronizedListメソッドを使用して明示的に同期させることができます。反復子が作成されてから、反復子自身のremoveまたはaddメソッド以外の方法で、反復子はConcurrentModificationExceptionをスローします。

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

1)上で説明したように、挿入と削除の操作は、ArrayList(O(n))と比較してLinkedListのパフォーマンスが良い(O(1))。そのため、アプリケーションで頻繁に追加や削除を行う必要がある場合は、LinkedListが最適です。

2)検索(getメソッド)操作はArrayList(O(1))では高速ですが、LinkedList(O(n))では高速ではないため、追加および削除操作や検索操作の要件が少ない場合は、ArrayListを使用するのが最善です。

35
NoNaMe

LinkedListの作者、Joshua Bloch:

実際にLinkedListを使用している人はいますか?私はそれを書きました、そして私はそれを使いません。

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

他の答えほど有益ではないという答えを残念に思いますが、私はそれが最も面白くて自明であると思いました。

33
Ruslan

ArrayListはランダムにアクセス可能ですが、LinkedListは要素を展開したり要素を削除したりするのに本当に安価です。ほとんどの場合、ArrayListは問題ありません。

大規模なリストを作成してボトルネックを測定したのでなければ、おそらく違いについて心配する必要はないでしょう。

32
Dustin

あなたのコードがadd(0)remove(0)を持っているならば、LinkedListを使ってください、そしてそれはaddFirst()removeFirst()メソッドです。それ以外の場合はArrayListを使用してください。

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

20
Jesse Wilson

これは古い投稿ですが、LinkedListDequeを実装していると言っている人が誰もいないとは信じられません。 Deque(とQueue)のメソッドを見てください。公正な比較が必要な場合は、LinkedListに対してArrayDequeを実行し、機能ごとの比較を行ってください。 

20
Ajax

これがArrayListLinkedList、そしてCopyOnWrite-ArrayListのBig-O記法です。

配列リスト

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)

これらに基づいて、あなたは何を選ぶべきか決める必要があります。 :)

18
Rajith Delantha

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

実施

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

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


2.パフォーマンス

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

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

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

    ArrayList の背後にある理由はLinkedListより速いのに対して、ArrayListは内部的に配列データ構造を使用しているため、ArrayListはその要素に対してインデックスベースのシステムを使用しているためです。

    LinkedList は、指定された要素インデックスのノードを取得するために先頭または末尾(どちらか近いほう)から反復するため、その要素に対するインデックスベースのアクセスを提供しません。

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

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

    ArrayList の場合、配列がいっぱいの場合、つまり最悪の場合、配列のサイズを変更して要素を新しい配列にコピーするための追加コストが発生します。それはO(1)です。

  • 削除(int)操作

    LinkedListにおける削除操作は一般にArrayListと同じ、すなわちO(n)である。

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

    ArrayList remove(int)メソッドでは、古い配列から新しい更新された配列に要素をコピーする必要があるため、その実行時間はO(n)です。


逆イテレータ

LinkedList descendingIterator()を使用して逆方向に反復することができます。 

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


4.初期容量

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

LinkedList は、初期容量なしで空リストを作成するだけです。


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

LinkedList内のノードは次および前のノードのアドレスを維持する必要があるため、 LinkedList のメモリオーバーヘッドはArrayListと比較して大きくなります。しながら 

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


出典

TL; DR 現代のコンピュータアーキテクチャのため、ArrayListはほぼすべてのユースケースで非常に効率的になります - したがって、LinkedListは非常にユニークで極端な場合を除いて避けるべきです。


理論的には、LinkedListにはadd(E element)に対するO(1)があります。

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

LinkedListは キャッシュ敵対者 データ構造であるため、実際のやり方は非常に異なります。パフォーマンスのPOVから - LinkedListCache-friendlyArrayListよりもパフォーマンスが良い場合がほとんどありません。

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

enter image description here

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

enter image description here

LinkedListが同じ仕事を成し遂げるのにより多くの時間がかかります。 ソースソースコード

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

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

  2. 2次 逆方向ポインタを保持するためにはLinkedListが必要です。これは、ArrayListと比較して、格納された値あたり3倍のメモリ消費を意味します。

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

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

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が悪い結果またはさらに悪いケースの結果を示すはずです。

enter image description here

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

シーケンシャルなメモリの大部分をコピーすることは、現代のCPUによって最適化された操作です - 理論を変えて、実際にもArrayList/Vectorをはるかに効率的にする


クレジット:ここに掲載されているすべてのベンチマークは KjellHedström によって作成されています。さらに多くのデータが 彼のブログにあります

12
Lior Bar-On

上記の他の良い引数に加えて、ArrayListRandomAccessを実装していますが、LinkedListQueueインターフェースを実装しています。

それで、どういうわけか彼らは効率と振る舞いの違いで若干異なる問題に対処します(彼らの方法のリストを見てください)。

12
PhiLho

Javaチュートリアル - リストの実装 を参照してください。

9
chharvey

配列リストは、基本的にアイテムなどを追加するためのメソッドを含む配列です(代わりにジェネリックリストを使うべきです)。これはインデクサーを介してアクセスできる項目の集まりです(例えば[0])。それはある項目から次の項目への進行を意味します。

リンクリストは、ある項目から次の項目への進行を指定します(項目a - >項目b)。配列リストでも同じ効果が得られますが、リンクリストでは、どの項目が前の項目に続くことになっているかが絶対的に示されます。 

8
kemiller2002

それはあなたがリストに対してもっと何をすることになるかによって異なります。

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

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

7

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

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

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

ArrayListに関しては、配列の重複をできるだけ少なくするために、少なくとも初期容量を持つコンストラクタを常に使用することに同意します。

6
gaijinco

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

重要 :JavaのLinkedListでは、これは正しくありません。参照してください Javaでリンクリストのための高速な連結方法はありますか?

6
Karussell

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

|---------------------|---------------------|--------------------|------------|
|      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    |
|--------------------------------------|
5

ArrayListLinkedListはどちらもList interfaceを実装しており、それらのメソッドと結果はほぼ同じです。ただし、要件によって違いがあり、違いはありません。

ArrayListとLinkedList

1)Search:ArrayList検索操作は、LinkedList検索操作に比べてかなり高速です。 ArrayListget(int index)O(1)のパフォーマンスを示し、LinkedListのパフォーマンスはO(n)です。

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

2)Deletion:LinkedList remove操作はO(1)性能を与えますが、ArrayListは可変性能を与えます:O(n)最悪の場合(最初の要素を削除する間)そしてO(1)(最後の要素を削除する間)。

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

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

3)Inserts Performance:LinkedList addメソッドはO(1)パフォーマンスを与えますが、ArrayListは最悪の場合O(n)を与えます。理由は削除の説明と同じです。

4)Memory Overhead:ArrayListはインデックスと要素データを保持し、LinkedListは要素データと隣接ノードへの2つのポインタを保持します。 

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

これらのクラスには、次のような類似点がほとんどありません。

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

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

  • 上で説明したように、挿入と削除の操作は、ArrayList(O(n))と比較してLinkedListで良いパフォーマンス(O(1))を与えます。

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

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

    したがって、追加や削除の操作が少なく、検索操作の要件が多い場合は、ArrayListを使用することをお勧めします。

5
Real73

ArrayListの操作get(i)はLinkedListよりも高速です。
ArrayList: Listインタフェースのサイズ変更可能な配列の実装
LinkedList: ListインターフェイスとDequeインターフェイスの二重リンクリストの実装

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

5
Amitābha

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

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

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

アプリ内で頻繁に検索操作を行う場合は、ArrayListを使用してください。頻繁に挿入や削除がある場合は、LinkedListを使用してください。 

5
Nesan Mano

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

ArrayListとLinkedListの最初の違いは、LinkedListがLinkedListによって支えられているのに対し、ArrayListはArrayによって支えられているという事実です。これにより、パフォーマンスがさらに向上します。

2)LinkedListはDequeを実装しています  

ArrayListとLinkedListのもう1つの違いは、Listインタフェースとは別に、LinkedListはDequeインタフェースも実装しています。これは、add()およびpoll()、その他いくつかのDeque関数に対する先入れ先出し操作を提供します。 3)ArrayListへの要素の追加ArrayListへの要素の追加は、Arrayのサイズ変更を引き起こさない場合は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を使用します。Entryは、データと前後2つのノードを格納するための静的ネストクラスで、ArrayListは単にArrayにデータを格納するだけです。

そのため、ArrayListの場合、LinkedListよりもメモリ要件が少なくて済みます。ただし、Arrayが別のArrayにコンテンツをコピーするときにArrayがサイズ変更操作を実行する場合は例外です。

Arrayが十分に大きいと、その時点で大量のメモリを消費し、ガベージコレクションを引き起こす可能性があります。

ArrayListとLinkedListの間の上記のすべての違いから、remove()またはget()より頻繁にadd()操作を行う場合を除いて、ArrayListはほとんどすべての場合においてLinkedListよりも適しています。

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

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

私の意見では、Javaの実用的な目的のほとんどで、LinkedListよりもArrayListを使用します。

3
Anjali Suman

Remove()とinsert()の両方とも、ArrayListsとLinkedListsの両方に対して、ランタイム効率がO(n)です。しかし、線形処理時間の背後にある理由は、2つの非常に異なる理由から来ています。

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

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

挿入と削除のどちらが速いかは、どちらが行われるかによって異なります。私たちが始めに近づくなら、LinkedListはより速くなるでしょう、なぜなら私たちは比較的少数の要素を通過しなければならないからです。終わりに近づくと、ArrayListの方が速くなります。一定時間でそこにたどり着き、それに続く残りのいくつかの要素を変更するだけでよいためです。 n個の要素を通過するほうがn個の値を移動するよりも速いため、正確に中央で処理するとLinkedListが速くなります。

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

1
pietz

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)です
一方リンクリスト内 
iterator.remove()is O(1) 

0
Randhawa

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

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;
}
0
Jose Martinez