Vector
はScalaコレクションパーティーに遅れており、影響力のあるすべてのブログ投稿がすでに残っているようです。
Java ArrayList
はデフォルトのコレクションです-LinkedList
を使用するかもしれませんが、アルゴリズムを熟考して最適化に十分注意を払った場合のみです。 Scalaでは、Vector
をデフォルトのSeq
として使用する必要がありますか、またはList
が実際に適切な場合に解決しようとしますか?
一般的なルールとして、デフォルトではVector
を使用します。すべてのalmostに対してList
よりも高速であり、自明ではないサイズのシーケンスではよりメモリ効率が高くなります。こちらをご覧ください ドキュメント 他のコレクションと比較したベクターの相対的なパフォーマンス。 Vector
を使用することにはいくつかの欠点があります。具体的には:
List
よりも遅いです(ただし、考えているほどではありません)Scala 2.10以前のもう1つの欠点は、パターンマッチングのサポートがList
の方が優れていたことでしたが、これは2.10で一般化された+:
および:+
エクストラクターで修正されました。
この質問にアプローチするより抽象的な、代数的な方法もあります:あなたは概念的にどのようなシーケンスを持っていますか?また、あなたはそれで何をしていますか概念的に? Option[A]
を返す関数が表示された場合、その関数にはそのドメインにいくつかの穴があることがわかります(したがって部分的です)。これと同じロジックをコレクションに適用できます。
タイプList[A]
のシーケンスがある場合、2つのことを効果的に主張しています。まず、私のアルゴリズム(およびデータ)は完全にスタック構造です。次に、このコレクションで行うことは、O(n)トラバーサルのみであると断言しています。これら2つは本当に密接に関連しています。逆に、タイプVector[A]
の何かがある場合、私が主張しているonlyことは、データが明確に定義された順序と有限の長さを持っているということです。したがって、アサーションはVector
で弱く、これにより柔軟性が高まります。
アルゴリズムを::
、List
、およびhead
のみで実装できる場合、tail
は非常に高速になります。 Javaのsplit
の代わりにList
を生成してJavaのArray
を打ち負かしたとき、私はそれに関するオブジェクトレッスンを受けました。
ただし、List
には根本的な問題があります。並列アルゴリズムでは機能しません。 List
を複数のセグメントに分割したり、効率的に連結することはできません。
並列処理をはるかにうまく処理できる他の種類のコレクションがあり、Vector
もその1つです。 Vector
は局所性にも優れています-List
にはありません-これはいくつかのアルゴリズムにとって本当のプラスになります。
したがって、考慮されるすべてのこと、Vector
が最良の選択ですただしそうでない場合他のコレクションのいずれかが望ましい特定の考慮事項があります-例えば、Stream
を選択する場合遅延評価とキャッシング(Iterator
は高速ですが、キャッシュしません)、またはList
が前述の操作で自然に実装されている場合。
ところで、特定のAPI(Seq
の::
など)、さらにはIndexedSeq
が必要な場合を除き、List
またはGenSeq
を使用することをお勧めします。またはGenIndexedSeq
は、アルゴリズムを並行して実行できる場合。
不変コレクションの場合、シーケンスが必要な場合の主な決定は、IndexedSeq
またはLinearSeq
のどちらを使用するかです。これにより、パフォーマンスの保証が異なります。 IndexedSeqは、要素の高速ランダムアクセスと高速操作を提供します。 LinearSeqは、head
を介した最初の要素のみへの高速アクセスを提供しますが、高速tail
操作も提供します。 (Seqドキュメントから取得。)
IndexedSeq
の場合、通常はVector
を選択します。 Range
sおよびWrappedString
sもIndexedSeqです。
LinearSeq
の場合、通常はList
またはその遅延対応のStream
を選択します。他の例は、Queue
sおよびStack
sです。
したがって、Javaの用語では、ArrayList
はScalaのVector
と同様に使用され、LinkedList
はScalaのList
と同様に使用されます。ただし、Scalaでは、VectorよりもListを頻繁に使用する傾向があります。これは、Scalaが、マッピング、折りたたみ、反復などのシーケンスのトラバースを含む関数のサポートをはるかに優れているためです。個々の要素にランダムにアクセスするのではなく、これらの関数を使用してリスト全体を操作する傾向があります。
ここでのステートメントの一部は、特にScalaのimmutable.VectorがArrayListのようなものであるという考えが紛らわしい、または間違っています。リストとベクターはどちらも不変で永続的な(つまり、「変更されたコピーを取得するための安い」)データ構造です。可変のデータ構造用である可能性があるため、合理的なデフォルトの選択はありませんが、それはむしろアルゴリズムが何をしているかに依存します。リストは単リンクリストであり、ベクターはベース32の整数トライです。つまり、32次のノードを持つ一種の検索ツリーです。この構造を使用すると、ベクターはほとんどの一般的な操作を合理的に高速に、つまりO(log_32( n))。これは、先頭/末尾のプリペンド、アペンド、更新、ランダムアクセス、分解に有効です。順次の反復は線形です。一方、リストは、線形反復と一定時間の前置、頭/尾の分解を提供します。他のすべては、一般に線形時間を要します。
これは、ほとんどすべての場合にベクターがリストの優れた代替品のように見えるかもしれませんが、多くの場合、プリペンド、分解、および反復が機能プログラムのシーケンスの重要な操作であり、これらの操作の定数はベクターのより複雑な構造に。いくつかの測定を行ったので、リストの反復は約2倍、リストのprependは約100倍、リストのhead/tailの分解は約10倍、traversableからの生成はベクトルの約2倍高速です。 (これはおそらく、要素を1つずつ追加または追加する代わりに、Builderを使用して構築するときにVectorが一度に32要素の配列を割り当てることができるためです)。もちろん、リストでは線形の時間を必要としますが、ベクターでは事実上一定の時間(ランダムアクセスまたは追加)を行うすべての操作は、大きなリストでは非常に遅くなります。
では、どのデータ構造を使用する必要がありますか?基本的に、4つの一般的なケースがあります。
多数のランダムアクセスとランダムな突然変異を伴う状況では、Vector
(または docs say – a Seq
)が適切な妥協案のようです。これも パフォーマンス特性 が示唆するものです。
また、Vector
クラスは、完全なオブジェクトに対してコピーオンライトを行う必要がないため、データの重複がほとんどない分散環境でうまく動作するようです。 (参照: http://akka.io/docs/akka/1.1.3/scala/stm.html#persistent-datastructures )
不変にプログラミングしていて、ランダムアクセスが必要な場合は、Seqを使用します(実際に頻繁に行うSetが必要な場合を除きます)。それ以外の場合、Listは動作が並列化できないことを除いて、うまく機能します。
不変のデータ構造が不要な場合は、ArrayListと同等のScalaであるため、ArrayBufferを使用します。