8

これらの列挙の 1 つは、他の列挙よりも高速ですか、それともほぼ同じですか? (C# の例)

ケース 1:

Dictionary<string, object> valuesDict;

// valuesDict loaded with thousands of objects

foreach (object value in valuesDict.Values) { /* process */ }

ケース 2:

List<object> valuesList;

// valuesList loaded with thousands of objects

foreach (object value in valuesList) { /* process */ }

アップデート:

バックグラウンド:

ディクショナリは、(リストを反復処理するのではなく) 他の場所でのキー付き検索に役立ちますが、辞書を反復処理する方がリストを処理するよりもはるかに遅い場合、その利点は減少します。

更新: 多くのアドバイスを受けて、私は自分でテストを行いました。

まずはこちらが結果です。以下がプログラムです。

コレクション全体を繰り返す Dict: 78 Keyd: 131 List: 76

キー検索コレクション Dict: 178 Keyd: 194 List: 142800

using System;
using System.Linq;

namespace IterateCollections
{
    public class Data
    {
        public string Id;
        public string Text;
    }

    public class KeyedData : System.Collections.ObjectModel.KeyedCollection<string, Data>
    {
        protected override string GetKeyForItem(Data item)
        {
            return item.Id;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var dict = new System.Collections.Generic.Dictionary<string, Data>();
            var list = new System.Collections.Generic.List<Data>();
            var keyd = new KeyedData();

            for (int i = 0; i < 10000; i++)
            {
                string s = i.ToString();
                var d = new Data { Id = s, Text = s };
                dict.Add(d.Id, d);
                list.Add(d);
                keyd.Add(d);
            }

            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            for (int r = 0; r < 1000; r++)
            {
                foreach (Data d in dict.Values)
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            var dictTime = sw.ElapsedMilliseconds;

            sw.Reset();
            sw.Start();
            for (int r = 0; r < 1000; r++)
            {
                foreach (Data d in keyd)
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            var keydTime = sw.ElapsedMilliseconds;

            sw.Reset();
            sw.Start();
            for (int r = 0; r < 1000; r++)
            {
                foreach (Data d in list)
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            var listTime = sw.ElapsedMilliseconds;

            Console.WriteLine("Iterate whole collection");
            Console.WriteLine("Dict: " + dictTime);
            Console.WriteLine("Keyd: " + keydTime);
            Console.WriteLine("List: " + listTime);

            sw.Reset();
            sw.Start();
            for (int r = 0; r < 1000; r++)
            {
                for (int i = 0; i < 10000; i += 10)
                {
                    string s = i.ToString();
                    Data d = dict[s];
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            dictTime = sw.ElapsedMilliseconds;

            sw.Reset();
            sw.Start();
            for (int r = 0; r < 1000; r++)
            {
                for (int i = 0; i < 10000; i += 10)
                {
                    string s = i.ToString();
                    Data d = keyd[s];
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            keydTime = sw.ElapsedMilliseconds;

            sw.Reset();
            sw.Start();
            for (int r = 0; r < 10; r++)
            {
                for (int i = 0; i < 10000; i += 10)
                {
                    string s = i.ToString();
                    Data d = list.FirstOrDefault(item => item.Id == s);
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            listTime = sw.ElapsedMilliseconds * 100;

            Console.WriteLine("Keyed search collection");
            Console.WriteLine("Dict: " + dictTime);
            Console.WriteLine("Keyd: " + keydTime);
            Console.WriteLine("List: " + listTime);

        }
    }

}

アップデート:

@Blamによって提案された辞書とKeyedCollectionの比較。

最も速い方法は、KeyedCollection アイテムの配列を反復処理することです。

ただし、配列に変換せずに KeyedCollection を反復処理するよりも、辞書の値を反復処理する方が高速であることに注意してください。

ディクショナリ値の反復は、ディクショナリ コレクションの反復処理よりもはるかに高速であることに注意してください。

 Iterate 1,000 times over collection of 10,000 items
   Dictionary Pair:   519 ms
 Dictionary Values:    95 ms
  Dict Val ToArray:    92 ms
   KeyedCollection:   141 ms
   KeyedC. ToArray:    17 ms

タイミングは、Windows コンソール アプリケーション (リリース ビルド) からのものです。ソースコードは次のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;

namespace IterateCollections
{
    public class GUIDkeyCollection : System.Collections.ObjectModel.KeyedCollection<Guid, GUIDkey>
    {
        // This parameterless constructor calls the base class constructor 
        // that specifies a dictionary threshold of 0, so that the internal 
        // dictionary is created as soon as an item is added to the  
        // collection. 
        // 
        public GUIDkeyCollection() : base() { }

        // This is the only method that absolutely must be overridden, 
        // because without it the KeyedCollection cannot extract the 
        // keys from the items.  
        // 
        protected override Guid GetKeyForItem(GUIDkey item)
        {
            // In this example, the key is the part number. 
            return item.Key;
        }

        public GUIDkey[] ToArray()
        {
            return Items.ToArray();
        }

        //[Obsolete("Iterate using .ToArray()", true)]
        //public new IEnumerator GetEnumerator()
        //{
        //    throw new NotImplementedException("Iterate using .ToArray()");
        //}
    }
    public class GUIDkey : Object
    {
        private Guid key;
        public Guid Key
        {
            get
            {
                return key;
            }
        }
        public override bool Equals(Object obj)
        {
            //Check for null and compare run-time types.
            if (obj == null || !(obj is GUIDkey)) return false;
            GUIDkey item = (GUIDkey)obj;
            return (Key == item.Key);
        }
        public override int GetHashCode() { return Key.GetHashCode(); }
        public GUIDkey(Guid guid)
        {
            key = guid;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            const int itemCount = 10000;
            const int repetitions = 1000;
            const string resultFormat = "{0,18}: {1,5:D} ms";

            Console.WriteLine("Iterate {0:N0} times over collection of {1:N0} items", repetitions, itemCount);

            var dict = new Dictionary<Guid, GUIDkey>();
            var keyd = new GUIDkeyCollection();

            for (int i = 0; i < itemCount; i++)
            {
                var d = new GUIDkey(Guid.NewGuid());
                dict.Add(d.Key, d);
                keyd.Add(d);
            }

            var sw = new System.Diagnostics.Stopwatch();
            long time;

            sw.Reset();
            sw.Start();
            for (int r = 0; r < repetitions; r++)
            {
                foreach (KeyValuePair<Guid, GUIDkey> w in dict)
                {
                    if (null == w.Value) throw new ApplicationException();
                }
            }
            sw.Stop();
            time = sw.ElapsedMilliseconds;
            Console.WriteLine(resultFormat, "Dictionary Pair", time);

            sw.Reset();
            sw.Start();
            for (int r = 0; r < repetitions; r++)
            {
                foreach (GUIDkey d in dict.Values)
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            time = sw.ElapsedMilliseconds;
            Console.WriteLine(resultFormat, "Dictionary Values", time);

            sw.Reset();
            sw.Start();
            for (int r = 0; r < repetitions; r++)
            {
                foreach (GUIDkey d in dict.Values.ToArray())
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            time = sw.ElapsedMilliseconds;
            Console.WriteLine(resultFormat, "Dict Val ToArray", time);

            sw.Reset();
            sw.Start();
            for (int r = 0; r < repetitions; r++)
            {
                foreach (GUIDkey d in keyd)
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            time = sw.ElapsedMilliseconds;
            Console.WriteLine(resultFormat, "KeyedCollection", time);

            sw.Reset();
            sw.Start();
            for (int r = 0; r < repetitions; r++)
            {
                foreach (GUIDkey d in keyd.ToArray())
                {
                    if (null == d) throw new ApplicationException();
                }
            }
            sw.Stop();
            time = sw.ElapsedMilliseconds;
            Console.WriteLine(resultFormat, "KeyedC. ToArray", time);
        }
    }

}
4

5 に答える 5

4

キーで検索したい場合は、Dictionary.
キーによる辞書検索は非常に高速です。

foreach の違いはわずかです。

キーがプロパティでもある場合は、KeyedCollection
KeyedCollection クラスを検討してください。

キーが値に埋め込まれているコレクションの抽象基本クラスを提供します。

Servy がコメントしたように、必要な機能を備えたコレクションを選択してください。.NET に
は多くのコレクションがあります。
System.Collections 名前空間

また、オブジェクトに Int32 として表現できる自然キーがある場合は、OverRide GetHashCode() を検討してください。

オブジェクトに GUID の自然キーがある場合は、KeyedCollection と OverRide GetHashCode と Equals を検討してください。

また、キー以外のプロパティの検索については、ForEach ではなく LINQ を検討してください。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace IntIntKeyedCollection
{
    class Program
    {
        static void Main(string[] args)
        {

            Guid findGUID = Guid.NewGuid();
            GUIDkeyCollection gUIDkeyCollection = new GUIDkeyCollection();
            gUIDkeyCollection.Add(new GUIDkey(findGUID));
            gUIDkeyCollection.Add(new GUIDkey(Guid.NewGuid()));
            gUIDkeyCollection.Add(new GUIDkey(Guid.NewGuid()));
            GUIDkey findGUIDkey = gUIDkeyCollection[findGUID];  // lookup by key (behaves like a dict)
            Console.WriteLine(findGUIDkey.Key);
            Console.WriteLine(findGUIDkey.GetHashCode());
            Console.WriteLine(findGUID);
            Console.WriteLine(findGUID.GetHashCode());
            Console.ReadLine();
        }
        public class GUIDkeyCollection : KeyedCollection<Guid, GUIDkey>
        {
            // This parameterless constructor calls the base class constructor 
            // that specifies a dictionary threshold of 0, so that the internal 
            // dictionary is created as soon as an item is added to the  
            // collection. 
            // 
            public GUIDkeyCollection() : base() { }

            // This is the only method that absolutely must be overridden, 
            // because without it the KeyedCollection cannot extract the 
            // keys from the items.  
            // 
            protected override Guid GetKeyForItem(GUIDkey item)
            {
                // In this example, the key is the part number. 
                return item.Key;
            }
        }
        public class GUIDkey : Object
        {
            private Guid key;
            public Guid Key
            {
                get
                {
                    return key;
                }
            }
            public override bool Equals(Object obj)
            {
                //Check for null and compare run-time types.
                if (obj == null || !(obj is GUIDkey)) return false;
                GUIDkey item = (GUIDkey)obj;
                return (Key == item.Key);
            }
            public override int GetHashCode() { return Key.GetHashCode(); }
            public GUIDkey(Guid guid)
            {
                key = guid;
            }
        }
    }
}
于 2013-04-09T14:31:12.903 に答える
4

ほぼ同時期です。プロセスにコードが含まれていれば、おそらく目立たなくなります。

しかし、なぜあなたはインターネットから無作為に人々に耳を傾けるのですか? 試して!

Stopwatchクラスが役立つ場合があります

于 2013-04-09T14:28:07.543 に答える
2

ここにあなたのテストがあります:

class speedtest
{
    static void Main(string[] args)
    {
        int size = 10000000;
        Dictionary<string, object> valuesDict = new Dictionary<string, object>();
        List<object> valuesList = new List<object>();

        for (int i = 0; i < size; i++)
        {
            valuesDict.Add(i.ToString(), i);
            valuesList.Add(i);
        }
        // valuesDict loaded with thousands of objects

        Stopwatch s = new Stopwatch();
        s.Start();
        foreach (object value in valuesDict.Values) { /* process */ }
        s.Stop();

        Stopwatch s2 = new Stopwatch();
        s2.Start();
        foreach (object value in valuesList) { /* process */ }
        s.Stop();
        Console.WriteLine("Size {0}, Dictonary {1}ms, List {2}ms",size,s.ElapsedMilliseconds,s2.ElapsedMilliseconds);

        Console.ReadLine();
    }
}

Outputs:
Size 10000000, Dictonary 73ms, List 63ms

ただし、辞書にハッシュ衝突があるかどうかもテストする必要があります。彼らはあなたに別の結果を与えることができます.

実際のアプリケーションの場合、構造の作成、アクセス、およびメモリ フット プリントに費やす時間を考慮する必要があります。

于 2013-04-09T14:41:16.277 に答える
2

他の人が言ったように、時期尚早の最適化ヤダヤダ。適切なシナリオには適切なコレクションを使用し、問題が発生した場合にのみ速度について心配してください。

とにかく、知る唯一の方法は測定することです。ディクショナリとリストに 30,000 個のオブジェクトを入力し、それらを 10,000 回反復する簡単で汚いテストを作成しました。結果は次のとおりです。

辞書: 2683ms
リスト: 1955ms

そのため、何らかの理由で Dictionary.Values を列挙するのが少し遅いようです。

コードは次のとおりです。

void Main()
{
    int i = 0;

    var dict = new Dictionary<string, object>();
    var list = new List<object>();

    for (i = 0; i < 30000; i++)
    {
        var foo = new Foo();
        dict.Add(i.ToString(), foo);
        list.Add(foo);
    }

    var dictWatch = new Stopwatch();

    dictWatch.Start();

    for (i = 0; i < 10000; i++)
    {
        foreach (var foo in dict.Values) {}
    }

    dictWatch.Stop();
    Console.WriteLine("Dictionary: " + dictWatch.ElapsedMilliseconds);

    var listWatch = new Stopwatch();
    listWatch.Start();

    for (i = 0; i < 10000; i++)
    {
        foreach (var foo in list) {}
    }

    listWatch.Stop();

    Console.WriteLine("List: " + listWatch.ElapsedMilliseconds);
}

class Foo {}
于 2013-04-09T14:45:36.783 に答える