List
には、要素を(不変の)リストに追加するために指定される2つのメソッドがあります。
+:
(Seq.+:
の実装)、および::
(List
でのみ定義)+:
技術的には、より一般的なタイプ署名があります—
def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That
def ::[B >: A](x: B): List[B]
—しかし、暗黙の無視は、docメッセージによれば、That
がList[B]
である必要があるだけであるため、署名は同等です。
List.+:
とList.::
の違いは何ですか?実際に同じ場合、+:
は具体的な実装List
に依存しないようにすることをお勧めします。しかし、なぜ別のパブリックメソッドが定義され、クライアントコードはいつそれを呼び出すのでしょうか?
パターンマッチングには::
の抽出機能もありますが、これらの特定のメソッドについては疑問に思っています。
両方の方法の違いを判断する最良の方法は、ソースコードを調べることです。
source of _::
_:
_def ::[B >: A] (x: B): List[B] =
new scala.collection.immutable.::(x, this)
_
source of _+:
_:
_override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That = bf match {
case _: List.GenericCanBuildFrom[_] => (elem :: this).asInstanceOf[That]
case _ => super.+:(elem)(bf)
}
_
ご覧のとおり、List
の場合、両方のメソッドが同じことを行います(コンパイラはCanBuildFrom
引数に List.canBuildFrom を選択します)。
それで、どの方法を使用するのですか?通常、実装(_+:
_)よりもインターフェイス(_::
_)を選択しますが、List
は関数型言語の一般的なデータ構造であるため、独自のメソッドが広く使用されています。多くのアルゴリズムは、List
の動作方法を構築します。たとえば、単一の要素をList
の前に追加したり、便利なhead
またはtail
メソッドを呼び出す多くのメソッドがあります。これらの操作はすべてO(1)
であるためです。 。したがって、ローカルでList
(単一のメソッドまたはクラス内)で作業する場合、List
固有のメソッドを選択しても問題はありません。ただし、クラス間で通信したい場合、つまりいくつかのインターフェイスを作成したい場合は、より一般的なSeq
インターフェイスを選択する必要があります。
+:
は、結果の型を呼び出し元のオブジェクトの型と異なるものにすることができるため、より一般的です。例えば:
scala> Range(1,4).+:(0)
res7: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 2, 3)