for
まず、パフォーマンスに関する問題は興味深いものであり、時期尚早の最適化のスマックです。この回答が示すように、ループとのパフォーマンスの数ミリ秒の違いを見ている可能性がありますToDictionary
。
これをリアルタイムシステムで実行していない限り、私は多くの問題を見ることができません。
ショーに移ります-以下は、辞書を作成するために私が考えることができる3つ(および半分)の異なる方法の大まかなベンチマーク(実際のタイミングのみが信頼できる)です。1つ目はfor
ループを使用し、2つ目は同じことを実行しますが、配列のLength
プロパティは使用しません(目的のため)。3番目と4番目の使用ToDictionary
; 1つはaSelect
を使用し、もう1つはカウンター変数(ハイブリッド)を使用します。
[TestMethod]
public void SomeBenchmark()
{
List<double> forLoopTimes = new List<double>();
List<double> forLoop2Times = new List<double>();
List<double> toDictionaryTimes = new List<double>();
List<double> hybridTimes = new List<double>();
string[] array = Enumerable.Range(0, 5000).Select(i => i.ToString()).ToArray();
Dictionary<int, string> dictionary;
int runCount = 5000;
int arrayLen = array.Length;
while (runCount-- != 0)
{
Stopwatch sw = Stopwatch.StartNew();
dictionary = new Dictionary<int, string>();
for (int i = 0; i < array.Length; i++)
{
dictionary[i] = array[i];
}
sw.Stop();
forLoopTimes.Add(sw.Elapsed.TotalMilliseconds);
sw.Restart();
dictionary = new Dictionary<int, string>();
for (int i = 0; i < arrayLen; i++)
{ //same as before - but using arrayLen instead of property
dictionary[i] = array[i];
}
sw.Stop();
forLoop2Times.Add(sw.Elapsed.TotalMilliseconds);
sw.Restart();
dictionary = array.Select((s, i) => new { Key = i, Value = s }).ToDictionary(v => v.Key, v => v.Value);
sw.Stop();
toDictionaryTimes.Add(sw.Elapsed.TotalMilliseconds);
int counter = 0;
sw.Restart();
dictionary = array.ToDictionary(s => counter++, s => s);
sw.Stop();
hybridTimes.Add(sw.Elapsed.TotalMilliseconds);
}
Console.WriteLine("for loop average: {0} milliseconds", forLoopTimes.Average());
Console.WriteLine("for loop(2) average: {0} milliseconds", forLoop2Times.Average());
Console.WriteLine("ToDictionary average: {0} milliseconds", toDictionaryTimes.Average());
Console.WriteLine("Hybrid average: {0} milliseconds", hybridTimes.Average());
}
結果(ビルドをリリースし、Dell 2.4Ghzワークステーションで実行するのに約20秒かかります):
ループ平均の場合:0.28880804ミリ秒
ループ(2)の平均:0.2773845ミリ秒
ToDictionary平均:0.479094339999998ミリ秒
ハイブリッド平均:0.353655779999999ミリ秒
したがって、ループは間違いなく高速です-最も近い実装for
の少なくとも22%です。ToDictionary
私は100,000の要素でそれを試しました、そしてそれはそれから約30%になります。
2番目のループの結果に注意してください-プロパティをバイパスすることは良い考えであるfor
ことを示唆しているようです。Length
確かに私は4回続けて実行しました、そしてこれらは結果です(上からの最初のものを含む):
Forループ:0.28880804、0.28562478、0.283770739999999、0.287241679999999
for loop(2):0.2773845、0.27621306、0.27869996、0.27962916
ToDictionary:0.479094339999998、0.476417939999997、0.476162219999997、0.475776479999997
ハイブリッド:0.353655779999999、0.3583224、0.352022739999998、0.349865779999999
ただし、少なくとも1つのベンチマーク結果についても結果が反転するのを見てきました。これは、この種のベンチマークがどれほど無意味であるかを示しています。現実的には、キャッシュなどを回避するために、テストごとに異なる配列も生成する必要があります。
別の方法があります。
IDictionary<int, string>
呼び出しているメソッドが(注-インターフェース)を受け入れる場合。インターフェースの必要なメンバーを実装する単純なラッパータイプを作成することはできませんDictionary<int, string>
。したがって、ディクショナリに投影する必要性を完全に回避できます。特定のメンバーのみが必要な場合に限ります。ほぼ完全な実装は次のとおりです。
public class FakeDictionary : IDictionary<int, string>
{
private readonly string[] _array;
public FakeDictionary(string[] array)
{
_array = array;
}
#region IDictionary<int,string> Members
public void Add(int key, string value)
{
throw new NotSupportedException();
}
public bool ContainsKey(int key)
{
return key >= 0 && key < _array.Length;
}
public ICollection<int> Keys
{
get { return Enumerable.Range(0, _array.Length).ToArray(); }
}
public bool Remove(int key)
{
throw new NotSupportedException();
}
public bool TryGetValue(int key, out string value)
{
value = null;
if (key >= 0 && key < _array.Length)
{
value = _array[key];
return true;
}
return false;
}
public ICollection<string> Values
{
get { return _array; }
}
public string this[int key]
{
get
{
try
{
return _array[key];
}
catch (ArgumentOutOfRangeException ex)
{
throw new KeyNotFoundException("Invalid key", ex);
}
}
set //note - can't be used to add items
{
try
{
_array[key] = value;
}
catch (ArgumentOutOfRangeException ex)
{
throw new KeyNotFoundException("Invalid key", ex);
}
}
}
#endregion
#region ICollection<KeyValuePair<int,string>> Members
public void Add(KeyValuePair<int, string> item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(KeyValuePair<int, string> item)
{
return ContainsKey(item.Key) && _array[item.Key].Equals(item.Value);
}
public void CopyTo(KeyValuePair<int, string>[] array, int arrayIndex)
{
//too much for an SO answer.
throw new NotImplementedException();
}
public int Count
{
get { return _array.Length; }
}
public bool IsReadOnly
{
//technically it's not - because we can modify individual elements -
//but at the collection-level it is
get { return true; }
}
public bool Remove(KeyValuePair<int, string> item)
{
throw new NotSupportedException();
}
#endregion
#region IEnumerable<KeyValuePair<int,string>> Members
public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}