.NETジェネリックディクショナリ<string、T>を複製/ディープコピーするための最良の方法は何ですか?


211

Dictionary<string, T>基本的に、あらゆる提案のClone()を作成したい汎用辞書があります。

回答:


184

さて、.NET 2.0の回答:

値を複製する必要がない場合は、既存のIDictionaryを取得するDictionaryへのコンストラクタオーバーロードを使用できます。(比較演算子を既存の辞書の比較演算子として指定することもできます。)

あなたがいる場合の値のクローンを作成する必要がある、あなたはこのようなものを使用することができます。

public static Dictionary<TKey, TValue> CloneDictionaryCloningValues<TKey, TValue>
   (Dictionary<TKey, TValue> original) where TValue : ICloneable
{
    Dictionary<TKey, TValue> ret = new Dictionary<TKey, TValue>(original.Count,
                                                            original.Comparer);
    foreach (KeyValuePair<TKey, TValue> entry in original)
    {
        ret.Add(entry.Key, (TValue) entry.Value.Clone());
    }
    return ret;
}

TValue.Clone()もちろん、これは適切なディープクローンであることにも依存しています。


でも、それは辞書の値の浅いコピーをしているだけだと思います。entry.Value値は、さらに別の[サブ]コレクションかもしれません。
ChrisW、2017年

6
@ChrisW:さて、それは各値を複製するように求めています-それがClone()深いか浅いかにかかわらず、それはメソッド次第です。その効果にメモを追加しました。
Jon Skeet 2017年

1
@SaeedGanji:値を複製する必要がない場合は、「既存のIDictionaryを取得するDictionaryへのコンストラクターオーバーロードを使用する」で問題ありません。値がいる場合複製する必要があり、その後、あなたがリンクされてきた答えは全く役に立ちません。
Jon Skeet、

1
@SaeedGanji:はい、大丈夫です。(もちろん、構造体に変更可能な参照型への参照が含まれている場合、それはまだ問題になる可能性があります...しかしうまくいけばそうではありません。)
Jon Skeet

1
@SaeedGanji:それは他に何が起こっているかに依存します。他のスレッドが元のディクショナリから読み取るだけの場合は、問題ないはずです。何かを変更している場合は、そのスレッドと複製スレッドの両方をロックして、それらが同時に発生しないようにする必要があります。辞書を使用するときにスレッドセーフが必要な場合は、を使用しますConcurrentDictionary
Jon Skeet、

210

(注:複製バージョンは潜在的に有用ですが、単純な浅いコピーの場合、他の投稿で言及したコンストラクターの方が優れたオプションです。)

コピーの深さはどのくらいにしますか?また、使用している.NETのバージョンは何ですか?.NET 3.5を使用している場合、キーと要素セレクターの両方を指定するToDictionaryへのLINQ呼び出しが最も簡単な方法になると思います。

たとえば、値が浅いクローンであることを気にしない場合:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
                                               entry => entry.Value);

ICloneableを実装するようにTを既に制限している場合:

var newDictionary = oldDictionary.ToDictionary(entry => entry.Key, 
                                               entry => (T) entry.Value.Clone());

(これらはテストされていませんが、動作するはずです。)


回答Jonをありがとう。フレームワークのv2.0を実際に使用しています。
mikeymo 2008

このコンテキストでの「entry => entry.Key、entry => entry.Value」とは何ですか。キーと値をどのように追加しますか?それは私の終わりにエラーを示しています
Pratik 2013

2
@Pratik:ラムダ式です-C#3の一部です。–
Jon Skeet

2
デフォルトでは、LINQのToDictionaryは比較子をコピーしません。他の回答で比較子をコピーすることについて説明しましたが、このバージョンの複製も比較子を渡す必要があると思います。
user420667

86
Dictionary<string, int> dictionary = new Dictionary<string, int>();

Dictionary<string, int> copy = new Dictionary<string, int>(dictionary);

5
値のポインタは同じままです。コピーの値に変更を適用すると、変更はディクショナリオブジェクトにも反映されます。
Fokko Driesprong 2012

4
@FokkoDriesprongいいえ、それは新しいオブジェクトにkeyValuePairsをコピーするだけです

17
これは間違いなくうまく機能します-キーと値の両方のクローンを作成します。もちろん、これは値が参照型ではない場合にのみ機能します。値が参照型の場合は、キーのコピーを浅いコピーとして取得するだけです。
コンタンゴ

1
@Contangoしたがって、この場合、stringとintは参照型ではないため、正しく機能しますか?
MonsterMMORPG

3
@UğurAldanmaz参照オブジェクトへの実際の変更をテストするのを忘れて、明らかに機能するクローン辞書の値ポインタの置換をテストするだけですが、テストオブジェクトのプロパティを変更しただけでは、テストは失敗します:dotnetfiddle.net / xmPPKr
Jens

10

.NET 2.0のためには、継承するクラス実装できるDictionaryと道具をICloneable

public class CloneableDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : ICloneable
{
    public IDictionary<TKey, TValue> Clone()
    {
        CloneableDictionary<TKey, TValue> clone = new CloneableDictionary<TKey, TValue>();

        foreach (KeyValuePair<TKey, TValue> pair in this)
        {
            clone.Add(pair.Key, (TValue)pair.Value.Clone());
        }

        return clone;
    }
}

その後、Cloneメソッドを呼び出すだけで辞書を複製できます。もちろん、この実装では、辞書の値の型がimplementsであることが必要ですICloneableが、それ以外の場合、一般的な実装はまったく実用的ではありません。


8

これは私にとってはうまくいきます

 // assuming this fills the List
 List<Dictionary<string, string>> obj = this.getData(); 

 List<Dictionary<string, string>> objCopy = new List<Dictionary<string, string>>(obj);

Tomer Wolbergがコメントで説明しているように、値の型が変更可能なクラスの場合、これは機能しません。


1
これは真剣に賛成票が必要です!ただし、元のディクショナリーが読み取り専用である場合、これは引き続き機能します。var newDict = readonlyDict.ToDictionary(kvp => kvp.Key、kvp => kvp.Value)
Stephan

2
値の型が変更可能なクラスの場合、これは機能しません
Tomer Wolberg

5

常にシリアル化を使用できます。オブジェクトをシリアル化してから逆シリアル化できます。これにより、辞書とその中のすべてのアイテムの詳細なコピーが得られます。これで、特別なコードを記述することなく、[Serializable]とマークされたオブジェクトのディープコピーを作成できます。

バイナリシリアル化を使用する2つの方法を次に示します。これらのメソッドを使用する場合、単に呼び出す

object deepcopy = FromBinary(ToBinary(yourDictionary));

public Byte[] ToBinary()
{
  MemoryStream ms = null;
  Byte[] byteArray = null;
  try
  {
    BinaryFormatter serializer = new BinaryFormatter();
    ms = new MemoryStream();
    serializer.Serialize(ms, this);
    byteArray = ms.ToArray();
  }
  catch (Exception unexpected)
  {
    Trace.Fail(unexpected.Message);
    throw;
  }
  finally
  {
    if (ms != null)
      ms.Close();
  }
  return byteArray;
}

public object FromBinary(Byte[] buffer)
{
  MemoryStream ms = null;
  object deserializedObject = null;

  try
  {
    BinaryFormatter serializer = new BinaryFormatter();
    ms = new MemoryStream();
    ms.Write(buffer, 0, buffer.Length);
    ms.Position = 0;
    deserializedObject = serializer.Deserialize(ms);
  }
  finally
  {
    if (ms != null)
      ms.Close();
  }
  return deserializedObject;
}

5

私にとって最良の方法はこれです:

Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);

3
辞書は参照型なので、これは値ではなく参照をコピーするだけではありませんか?つまり、一方の値を変更すると、もう一方の値も変更されますか?
悟空

3

バイナリシリアル化方法は問題なく機能しますが、私のテストでは、クローンの非シリアル化実装よりも10倍遅いことが示されました。それをテストしたDictionary<string , List<double>>


完全なディープコピーを実行してもよろしいですか?文字列とリストの両方を深くコピーする必要があります。それが遅くなることを引き起こす直列化バージョンではいくつかのバグもあります。中にメソッドで呼び出される代わりに。次に、byte []が最初に手動でMemStreamにコピーされますが、コンストラクタに提供するだけでかまいません。ToBinary()Serialize()thisyourDictionaryFromBinary()
ジュピター

1

ディクショナリー<文字列、文字列>をディープコピーしようとしたときに、それが役に立ちました。

Dictionary<string, string> dict2 = new Dictionary<string, string>(dict);

幸運を


.NET 4.6.1で正常に動作します。これが更新された答えになるはずです。
タラルカズミ

0

キー/値がICloneableの場合は、これを試してください。

    public static Dictionary<K,V> CloneDictionary<K,V>(Dictionary<K,V> dict) where K : ICloneable where V : ICloneable
    {
        Dictionary<K, V> newDict = null;

        if (dict != null)
        {
            // If the key and value are value types, just use copy constructor.
            if (((typeof(K).IsValueType || typeof(K) == typeof(string)) &&
                 (typeof(V).IsValueType) || typeof(V) == typeof(string)))
            {
                newDict = new Dictionary<K, V>(dict);
            }
            else // prepare to clone key or value or both
            {
                newDict = new Dictionary<K, V>();

                foreach (KeyValuePair<K, V> kvp in dict)
                {
                    K key;
                    if (typeof(K).IsValueType || typeof(K) == typeof(string))
                    {
                        key = kvp.Key;
                    }
                    else
                    {
                        key = (K)kvp.Key.Clone();
                    }
                    V value;
                    if (typeof(V).IsValueType || typeof(V) == typeof(string))
                    {
                        value = kvp.Value;
                    }
                    else
                    {
                        value = (V)kvp.Value.Clone();
                    }

                    newDict[key] = value;
                }
            }
        }

        return newDict;
    }

0

古い投稿に返信しましたが、次のようにラップすると便利です。

using System;
using System.Collections.Generic;

public class DeepCopy
{
  public static Dictionary<T1, T2> CloneKeys<T1, T2>(Dictionary<T1, T2> dict)
    where T1 : ICloneable
  {
    if (dict == null)
      return null;
    Dictionary<T1, T2> ret = new Dictionary<T1, T2>();
    foreach (var e in dict)
      ret[(T1)e.Key.Clone()] = e.Value;
    return ret;
  }

  public static Dictionary<T1, T2> CloneValues<T1, T2>(Dictionary<T1, T2> dict)
    where T2 : ICloneable
  {
    if (dict == null)
      return null;
    Dictionary<T1, T2> ret = new Dictionary<T1, T2>();
    foreach (var e in dict)
      ret[e.Key] = (T2)(e.Value.Clone());
    return ret;
  }

  public static Dictionary<T1, T2> Clone<T1, T2>(Dictionary<T1, T2> dict)
    where T1 : ICloneable
    where T2 : ICloneable
  {
    if (dict == null)
      return null;
    Dictionary<T1, T2> ret = new Dictionary<T1, T2>();
    foreach (var e in dict)
      ret[(T1)e.Key.Clone()] = (T2)(e.Value.Clone());
    return ret;
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.