26

私の現在のユースケースは非常に簡単で、可変または不変のMapでうまくいきます。

不変のマップを取得するメソッドがあります。このメソッドは、不変のマップも取得するサードパーティのAPIメソッドを呼び出します。

def doFoo(foo: String = "default", params: Map[String, Any] = Map()) {

  val newMap = 
    if(someCondition) params + ("foo" -> foo) else params

  api.doSomething(newMap)
}

問題のマップは一般に非常に小さく、多くてもケースクラスインスタンスのリストが埋め込まれている可能性があり、最大で数千のエントリがあります。したがって、この場合も、不変になることによる影響はほとんどないと想定します(つまり、newMap valコピーを介してマップのインスタンスが本質的に2つある)。

それでも、いくつかのk-> vエントリが追加された新しいマップを取得するためだけに、マップをコピーして、少し困惑します。

params.put("bar", bar)タックしたいエントリに対して可変などを実行し、次にparams.toMapapi呼び出しに対して不変に変換することができます。これはオプションです。しかし、その後、可変マップをインポートして渡す必要があります。これは、Scalaのデフォルトの不変マップを使用する場合に比べて少し面倒です。

では、不変のマップよりも可変のマップを使用することが正当化/グッドプラクティスである場合の一般的なガイドラインは何ですか?

ありがとう

そのように編集 すると、不変マップでの追加操作にはほぼ一定の時間がかかり、完全なコピーが作成されないという@dhgと@Nicolasの主張を確認するように見えます。これにより、提示された具体的なケースの問題が解決されます。

4

4 に答える 4

42

不変のマップの実装によっては、いくつかのエントリを追加しても、実際には元のマップ全体がコピーされない場合があります。これは、不変のデータ構造アプローチの利点の1つです。Scalaは、コピーをできるだけ少なくしようとします。

この種の動作は、で最も簡単に確認できListます。がある場合val a = List(1,2,3)、そのリストはメモリに保存されます。ただし、のような追加の要素を追加するval b = 0 :: aと、新しい4要素が返されますが、Scalaは元のリストをコピーしませんでした。代わりに、それと呼ばれる1つの新しいリンクを作成し、既存のリストへのポインターを指定しました。Listaba

他の種類のコレクションについても、このような戦略を想像することができます。たとえば、に1つの要素を追加するMapと、コレクションは既存のマップを単純にラップし、必要に応じてフォールバックし、APIを単一ののように提供しますMap

于 2012-04-13T13:27:13.027 に答える
14

可変オブジェクトを使用すること自体は悪いことではありません。関数型プログラミング環境では、関数を純粋に保ち、オブジェクトを不変に保つことで副作用を回避しようとする場合、それは悪くなります。

ただし、関数内で可変オブジェクトを作成してこのオブジェクトを変更した場合、関数外でこのオブジェクトへの参照を解放しなければ、関数は純粋なままです。次のようなコードを使用することは許容されます。

def buildVector( x: Double, y: Double, z: Double ): Vector[Double] = {
    val ary = Array.ofDim[Double]( 3 )
    ary( 0 ) = x
    ary( 1 ) = y
    ary( 2 ) = z
    ary.toVector
}

さて、このアプローチは次の2つの場合に役立つ/推奨されると思います。(1)不変オブジェクトの作成と変更がアプリケーション全体のボトルネックである場合、パフォーマンス。(2)コードの読みやすさ。複雑なオブジェクトを所定の位置で変更する方が(レンズやジッパーなどに頼るよりも)簡単な場合があるためです。

于 2012-04-13T15:00:28.850 に答える
5

dhgの答えに加えて、scalaコレクションのパフォーマンスを見ることができます。追加/削除操作に直線的な時間がかからない場合は、構造全体を単にコピーする以外の何かを行う必要があります。(逆は正しくないことに注意してください。構造全体をコピーするのに線形時間がかかるためではありません)

于 2012-04-13T13:50:40.337 に答える
0

可変または不変のマップではなく、宣言されたパラメータータイプ(入力値または戻り値)としてcollections.mapsを使用するのが好きです。コレクションマップは、両方のタイプの実装で機能する不変のインターフェイスです。マップを使用するコンシューマーメソッドは、実際には、マップの実装やマップの構築方法について知る必要はありません。(とにかく、それは実際にはそのビジネスのどれでもありません)。

マップの特定の構造(可変または不変)をそれを使用するコンシューマーから隠すというアプローチを採用した場合でも、本質的に不変のマップをダウンストリームで取得できます。また、collection.Mapを不変のインターフェイスとして使用することで、immutable.Map型のオブジェクトを使用するように作成されたコンシューマーで発生する「.toMap」の非効率性をすべて完全に取り除くことができます。最初のマップでサポートされていないインターフェイスに準拠するために、完全に構築されたマップを別のマップに変換する必要があることは、考えてみるとまったく不要なオーバーヘッドです。

これから数年以内に、3つの別々のインターフェイスセット(可変マップ、不変マップ、およびコレクションマップ)を振り返り、99%の時間で実際に必要なのは2つ(可変およびコレクション)であることに気付くと思います。 (残念ながら)デフォルトの不変マップインターフェイスを使用すると、「スケーラブル言語」に多くの不要なオーバーヘッドが実際に追加されます。

于 2016-06-03T17:54:25.087 に答える