web-dev-qa-db-ja.com

リストから最初の要素を取得/削除する効率的な方法?

リストから最初の要素を取り出して削除したい。わかりました。2つのオプションがあります。

最初のアプローチ:

LinkedList<String> servers = new LinkedList<String>();
....
String firstServerName = servers.removeFirst();

セカンドアプローチ

ArrayList<String> servers = new ArrayList<String>();
....
String firstServerName = servers.remove(0);

リストにはたくさんの要素があります。

  • どれを使うべきかという好みはありますか?
  • 上記の2つの違いは何ですか?パフォーマンスの点で技術的に同じですか?要素がたくさんある場合、複雑さはどの程度ですか?

これを行う最も効率的な方法は何ですか。

9
user1950349

「最初に削除」の比較がArrayListクラスとLinkedListクラスの間である場合、LinkedListが明らかに優先されます。

リンクリストから要素を削除するとO(1)がかかりますが、配列(配列リスト)を削除するとO(n)がかかります。

5
PNS

LinkedListとArrayListの違いを理解してください。 ArrayListは、Arrayを使用して実装されます。

LinkedListは要素を削除するのに一定の時間を要します。 ArrayListは、最初の要素を削除するために線形時間を要する場合があります(Javaエキスパート)ではなく、実装を確認する必要があることを確認するため)。

また、LinkedListはスペースの点でより効率的だと思います。 ArrayListは、要素が削除されるたびに配列のサイズを変更しない(そして変更してはならない)ため、必要以上のスペースを使用します。

5
Zlol

実際には、LinkedList#removeFirstは、二重にリンクされたリストで動作し、最初の要素の削除は基本的にリストの先頭からリンクを解除して次の要素を最初の要素に更新するだけなので、より効率的です。

private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

ArrayList#removeは、サブ配列をコピーして後続のすべての要素を左に1桁シフトする必要がある内部配列を操作しています。

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

一方、 LinkedList#get操作では、指定されたインデックスにある要素を取得するために、リスト全体の半分を走査する必要があります-最悪の場合のシナリオ。 ArrayList#getは配列を操作するため、指定されたインデックスの要素に直接アクセスします。

ここでの効率の目安は次のとおりです。

  • 検索操作と比較して多くのLinkedList/addを行う場合は、removeを使用します(例:get);
  • ArrayList/addと比較して多くの検索操作を行う場合は、removeを使用します。

他の人が正しく指摘しているように、LinkedListは、非常に短いリスト以外のものから最初の要素を削除する場合、ArrayListよりも高速です。

ただし、それらの間で選択を行うには、操作の完全な組み合わせを考慮する必要があります。たとえば、ワークロードが最初の要素を削除するたびに100の要素リストへの数百万のインデックス付きアクセスを行う場合、ArrayListは全体的に改善されます。

3

リンクされたリストを使用すると、はるかに高速です。

LinkedList

最初のノードが消えるようにノードを参照するだけです。 enter image description here

ArrayList

配列リストの場合、基になる配列を適切に保つために、すべての要素を1スポット戻す必要があります。

3
J Blaz

必要なのはArrayDequeJava.utilで見過ごされがちな見過ごされているクラス)だと思います。そのremoveFirstメソッドはLinkedListの場合と同様にO(1)で実行されますが、一般にArrayListのより良い空間と時間特性を示します。配列の循環キューとして実装されます。

LinkedListを使用することはほとんどありません。 Javaプログラマーとして17年間に1回行い、振り返って後悔しました。

3
Ole V.V.

ArrayListの最初の要素の削除はO(n)です。リンクされたリストはO(1)なので、それを使用します。

ArrayListを確認してください ドキュメント

Size、isEmpty、get、set、iterator、listIteratorの各操作は一定の時間で実行されます。追加操作は償却一定時間で実行されます。つまり、n個の要素を追加するにはO(n)時間を必要とします。他のすべての操作は線形時間で実行されます(おおまかに言えば)。定数係数は、LinkedList実装の係数と比較して低いです。

この人たちは実際にOpenJDKソースを入手しました link

3
Cacho Santa

ArrayList の場合、最初の要素を削除すると複雑度O(n)になるため、代わりに最初の要素を取得して、削除する代わりに List.subList(int fromIndex、 int toIndex)

final String firstServerName = servers.get(0);
servers = servers.subList(1, servers.size());
2
Roland

番目のアプローチ

これは、Java.util.Queueインターフェースによって公開されます。 LinkedListは、このインターフェースの実装です。

Queueインターフェースは、リスト(Queue)の先頭を効果的に削除するE poll()メソッドを公開しています。

パフォーマンスの点では、poll()メソッドはremoveFirst()と同等です。実際には、内部でremoveFirst()メソッドを使用しています。

1