Listは使わずにSeqにしなさいとよく言われるけど、何故そうなのかはいまいちよくわかってなかったので、調べました。基本的な内容です。
そもそもSeqとは
Seq(scala.collection.Seq)は、Iterableのうち順序を持つものを指します。
全てのcollectionはIterableであるので、順序がある(要素にindexでアクセスできる)コレクションは全てSeqです。(SeqでないコレクションにはMapやSetがあります)
Seqはscala.collection下にあるので、VectorだろうとMutableListだろうとSeqです。メソッドや関数の引数の型には、特別な理由がない限り、取りうる型の範囲を狭めてしまうListなどよりSeqを指定したほうが良さそうです。
scala> Seq
res0: scala.collection.Seq.type = scala.collection.Seq$@5680a178
scala> List
res1: scala.collection.immutable.List.type = scala.collection.immutable.List$@2d6a9952
IndexedSeqとLinearSeq
Seqは2種類のサブトレイトに分かれます。
- IndexedSeq : 要素へのランダムアクセスとlengthが速い
- LinearSeq : head/tailが速い
と、それぞれ異なるパフォーマンス特性を保証しています。
(immutableの方では)IndexedSeqのデフォルト実装はVectorであり、LinearSeqのデフォルト実装はListです。また、単純にSeqを作ったときもListが作成されます。
scala的にはindexアクセスはあまりしないでしょうから、SeqのデフォルトがLinearSeqなのは合理的と言えます。ちなみに、mutable.Seqはmutable.IndexedSeqと同じArrayBufferを作成します。
scala> Seq(1)
res2: Seq[Int] = List(1)
scala> IndexedSeq(1,2)
res3: IndexedSeq[Int] = Vector(1, 2)
基本的には「Listが使いたい」ことはあまりなくて、「先頭からのアクセスが速い配列が使いたい」ことのほうが多いとおもうので、scalaで配列的なものが使いたいときは、その使い方に応じてLinearSeq(Seq)かIndexedSeqを使うようにすれば良さそうです。
まとめ
- 引数の型にはListよりSeqを使ったほうがいい
- コンストラクタもListよりSeqを使ったほうがいい