ArrayDequeがLinkedListよりも優れている理由


160

どちらもDequeインターフェースを実装しているため、JavaのArrayDequeがJavaのLinkedListより優れている理由を理解しようとしています。

コードでArrayDequeを使用している人はほとんど見ません。誰かがArrayDequeの実装方法にもっと光を当てれば、それは役に立ちます。

理解できれば、もっと自信を持って使えます。ヘッドリファレンスとテールリファレンスの管理方法について、JDK実装を明確に理解できませんでした。


5
:日前に行わこの質問のIで答えを見stackoverflow.com/questions/6129805/...
レナート・Dinhani

1
jdkがインストールされているフォルダーに、ファイルがありますsrc.zip。これは、Javaクラスのソースコードを含むアーカイブです。これらのクラスの構造と内部構造を研究して、Javaクラスのしくみをよりよく理解することを強くお勧めします。

回答:


155

リンクされた構造は、各要素のキャッシュミスで反復する最悪の構造である可能性があります。その上、メモリをかなり消費します。

両端の追加/削除が必要な場合、ArrayDequeはリンクリストよりもはるかに優れています。各要素のランダムアクセスも循環キューのO(1)です。

リンクリストの唯一の優れた操作は、反復中に現在の要素を削除することです。


56
留意すべきもう1つの違い:LinkedListはnull要素をサポートしますが、ArrayDequeはサポートしません。
ルークアッシャーウッド2013

14
また、もう1つの小さな欠点(リアルタイムアプリケーションの場合)は、プッシュ/追加操作で、ArrayDequeの内部配列がいっぱいになると、サイズを2倍にしてすべてのデータをコピーする必要があるため、少し時間がかかることです。
Andrei I

4
@AndreiI、これは物語の片側だけです。リアルタイムアプリケーションの反復コストと、必要な容量を事前に割り当てる機能を除外しても、GCはLinkedList全体を反復する必要がある場合があります。基本的に、コスト(起動するにはより高い)をGCに移動します。
bestss 2014年

3
@DavidT。b / c解放されたノードのGCコストが含まれ、ヘッドの割り当てにはカードマーキングも必要になる場合があります(LinkedListがすでに期限付きの世代にある場合は、GCに対して再度)...そして、それは追加の間接参照(キャッシュ- miss)要素を返し、再リンクします。
bestsss 2014年

1
また、LinkedList実装ListしますArrayDequeが実装しません。この手段LinkedList方法を持っているが好きindexOfか、remove(int)しばらくはArrayDeque持っていません。それは時々重要になることがあります。
ZhekaKozlov、2015

60

パフォーマンスの主なボトルネックLinkedListは、dequeのいずれかの端にプッシュするたびに、実装が新しいリンクリストノードを割り当てることであり、これは本質的にJVM / OSを含み、コストがかかると私は思います。また、いずれかの端からポップするときはいつでも、の内部ノードがLinkedListガベージコレクションの対象となり、それは舞台裏での作業になります。また、リンクリストノードはあちこちに割り当てられているため、CPUキャッシュを使用してもあまりメリットはありません。

興味があれば、私は要素を追加(追加)するArrayListArrayDeque、償却済みの一定時間内に実行するという証拠を持っています。これを参照しください。


26

ArrayDeque 多くのコード(特に以前のバージョンのJavaとの互換性を確保しようとするプロジェクト)がJava 6を使用しないのはそのためです。

挿入するアイテムごとにノードを割り当てていないため、場合によっては「良い」場合があります。代わりに、すべての要素が巨大な配列に格納され、配列がいっぱいになるとサイズが変更されます。


17

Java LinkedListを批判しているすべての人は、ListJava を使用している他のすべての人がおそらくJava を使用していることを考える ArrayListと、LinkedListほとんどの場合、Java 6より前であり、それらはほとんどの本で最初から教えられているためです。

しかし、それは私が盲目的にLinkedList「s ArrayDeque」または「s」の側をとるという意味ではありません。知りたい場合は、以下のBrianのベンチマークをご覧ください。

テストのセットアップでは、次のことを考慮します。

  • 各テストオブジェクトは500文字の文字列です。各文字列は、メモリ内の異なるオブジェクトです。
  • テストアレイのサイズは、テスト中に変化します。
  • 配列サイズとキュー実装の組み合わせごとに、100個のテストが実行され、テストごとの平均時間が計算されます。
  • 各テストは、各キューをすべてのオブジェクトで満たしてから、それらをすべて削除することで構成されます。
  • 時間をミリ秒で測定します。

テスト結果:

  • 要素数が10,000未満の場合、LinkedListテストとArrayDequeテストはどちらも平均で1ミリ秒未満のレベルです。
  • データのセットが大きくなると、ArrayDequeとLinkedListの平均テスト時間の差が大きくなります。
  • 要素のテストサイズが9,900,000の場合、LinkedListアプローチはArrayDequeアプローチよりも165%長くかかりました。

グラフ:

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

取り除く:

  • 要件が100または200の要素を格納する場合、どちらのキューを使用しても大きな違いはありません。
  • ただし、モバイルで開発している場合は、厳密なメモリ制約のためにリストが必要とする可能性のある最大容量を推測したり、最大容量を使用し ArrayListたりArrayDequeすることができます。
  • 多くのコードが存在し、特にインターフェイスを実装していないためLinkedListArrayDeque特に使用することを決定するときに慎重に記述されていListます(十分な理由があると思います)。コードベースがListインターフェースと広範囲に通信する可能性があります。おそらく、あなたはでジャンプすることにしますArrayDeque。内部実装にそれを使用するのは良い考えかもしれません...

このベンチマークは、リンクリストのガベージによって引き起こされたGC時間をどのようにキャプチャしますか?
0xbe5077ed 2017

3

ArrayDequeLinkedListDequeインターフェースを実装していますが、実装は異なります。

主な違い:

  1. ArrayDequeクラスは、サイズ変更可能な配列の実装でのDequeインタフェースとのLinkedListのクラスは、リストの実装であります

  2. NULL要素をに追加することができLinkedListのではなく、中にArrayDeque

  3. ArrayDequeは、より効率的であるLinkedListの両端の追加および削除動作のためとLinkedListの実装は反復中に現在の要素を除去するための効率的です

  4. LinkedListのの実装は、より多くのメモリを消費ArrayDequeを

したがって、NULL要素をサポートする必要がなく、&&より少ないメモリを探している&&両端で要素を追加/削除する効率が良い場合は、ArrayDequeが最適です

詳細については、ドキュメントを参照してください。


13
「リンクされたリストでは、最後の要素を見つけるにはO(N)が必要です。」正確には真実ではありません。LinkedListは二重にリンクされたリストとして実装されるため、リストを走査して最後の要素(header.previous.element)を取得する必要はありません。バッキングアレイは常に次の2の累乗にサイズ変更されるため、「メモリ効率」の主張にも異議を唱えることができます。
ClémentMATHIEU

5
"最後の要素を見つけるにはO(N)が必要です"は間違っています。リンクリストは最後のノードへの参照を保持し、LinkedList.descendingIterator()はそのノードを取得します。したがって、O(1)のパフォーマンスが得られます。参照:coffeeorientedprogramming.wordpress.com/2018/04/23/…(したがって、反対投票)。
レオUfimtsev

1
を使用しIteratorて最後の要素にアクセスする場合、操作は両方のクラスでO(N)です。共通Dequeインターフェースを使用する場合、最後の要素へのアクセスは両方のクラスでO(1)です。どちらの観点から見ても、O(1)をArrayDequeO(N)にLinkedList同時に帰することは間違っています。
Holger

すべての提案が採用され、内容が修正されます
Ravindra babu

1

ただしArrayDeque<E>LinkedList<E>両方ともDeque<E>Interfaceを実装しています が、ArrayDequeは基本的にObject配列E[]を使用してエレメントをObject内に保持するため、通常はインデックスを使用してヘッドエレメントとテールエレメントを特定します。

つまり、Dequeのように(すべてのDequeのメソッドを使用して)機能しますが、配列のデータ構造を使用します。どちらがより良いかに関しては、それらをどのようにどこで使用するかによります。


-1

いつもそうとは限りません。

たとえば、以下のケースでlinkedlistArrayDeque、leetcode 103によるよりもパフォーマンスが優れています。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> rs=new ArrayList<>();
        if(root==null)
            return rs;
        // 👇 here ,linkedlist works better
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        boolean left2right=true;
        while(!queue.isEmpty())
        {
            int size=queue.size();
            LinkedList<Integer> t=new LinkedList<>();
            while(size-->0)
            {
                TreeNode tree=queue.remove();
                if(left2right)  
                    t.add(tree.val);
                else
                    t.addFirst(tree.val);
                if(tree.left!=null)
                {
                    queue.add(tree.left);
                }
                if(tree.right!=null)
                {
                    queue.add(tree.right);
                }
            }
            rs.add(t);
            left2right=!left2right;
        }
        return rs;
    }
}

-5

要素にアクセスするためのArrayDequeの時間の複雑さはO(1)であり、最後の要素にアクセスするためのLinkListの時間の複雑さはO(N)です。ArrayDequeはスレッドセーフではないため、複数のスレッドを介してアクセスできるように手動で同期する必要があります。


3
LinkedListJavaで参照している場合Collection、それは二重にリンクされており、先頭と末尾にすばやくアクセスできるため、最後の要素へのアクセスにもO(1)がかかります。
モーリス

LinkedListの最後の要素へのアクセスはO(N)ではありません。descendingIterator()を使用すると、O(1)で実行されます。coffeeorientedprogramming.wordpress.com/2018/04/23/… (したがって、反対票)を参照してください。
レオUfimtsev

1
どちらのクラスもスレッドセーフではありません。また、「手動で同期する必要があります」という文の最初と「より速い」という文の終わりの間には関係がありません。
Holger
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.