.net 2で一意のセットを生成する最速の方法は何ですか
-
04-07-2019 - |
質問
本質的に名前と値のペアのギザギザの配列があります-これから一意の名前の値のセットを生成する必要があります。ギザギザの配列は約86,000 x 11の値です。
名前と値のペア(単一の文字列<!> quot; name = value <!> quot;またはKeyValuePairなどの特殊なクラス)を保存する方法は、私には関係ありません。
追加情報: 40個の異なる名前と、より多くの異なる値があります-おそらく10,000個の値です。
C#と.NET 2.0を使用しています(パフォーマンスが非常に低いため、ジャグ配列全体をsqlデータベースにプッシュして、そこから個別に選択する方が良いと考えています)。
以下は現在使用しているコードです:
List<List<KeyValuePair<string,string>>> vehicleList = retriever.GetVehicles();
this.statsLabel.Text = "Unique Vehicles: " + vehicleList.Count;
Dictionary<KeyValuePair<string, string>, int> uniqueProperties = new Dictionary<KeyValuePair<string, string>, int>();
foreach (List<KeyValuePair<string, string>> vehicle in vehicleList)
{
foreach (KeyValuePair<string, string> property in vehicle)
{
if (!uniqueProperties.ContainsKey(property))
{
uniqueProperties.Add(property, 0);
}
}
}
this.statsLabel.Text += "\rUnique Properties: " + uniqueProperties.Count;
解決
9分以上から0.34秒で実行しています
問題は、KeyValuePair構造体を比較する場合です。 比較オブジェクトを作成し、そのインスタンスを辞書に渡すことで回避しました。
私が判断できるものから、KeyValuePair.GetHashCode()はそのKey
オブジェクト(この例では最も一意性の低いオブジェクト)のハッシュコードを返します。
ディクショナリは各アイテムを追加(および存在をチェック)するため、Equals関数とGetHashCode関数の両方を使用しますが、ハッシュコードの一意性が低い場合はEquals関数に依存する必要があります。
よりユニークなGetHashCode関数を提供することにより、Equals関数の実行頻度が大幅に低下します。また、Equals関数を最適化して、一意性の低いキーの前に一意の値を比較しました。
86,000 * 10,000個の一意のプロパティを持つ11個のアイテムは、下の比較オブジェクトを使用して0.34秒で実行されます(比較オブジェクトなしでは9分22秒かかります)
これが役立つことを願って:)
class StringPairComparer
: IEqualityComparer<KeyValuePair<string, string>>
{
public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
{
return x.Value == y.Value && x.Key == y.Key;
}
public int GetHashCode(KeyValuePair<string, string> obj)
{
return (obj.Key + obj.Value).GetHashCode();
}
}
EDIT :文字列が1つの文字列である場合(KeyValuePairではなく、文字列=名前+値)、約2倍の速度になります。これはすてきな興味をそそる問題であり、 faaaaaarにあまりにも多くの時間を費やしました(私は少し静かに学びました)
他のヒント
各キー/値ペアと生成する一意の値との間に特定の相関関係が必要ない場合は、GUIDを使用できますか?問題は、現在の「キー」がこのギザギザの配列で一意ではないことだと思います。
Dictionary<System.Guid, KeyValuePair<string, string>> myDict
= new Dictionary<Guid, KeyValuePair<string, string>>();
foreach of your key values in their current format
myDict.Add(System.Guid.NewGuid(), new KeyValuePair<string, string>(yourKey, yourvalue))
必要なものを保存するように聞こえますが、Guid <!> ampの生成との間にセマンティックな関係がないため、これからデータを取得する方法はわかりません。元々持っていたもの...
質問にさらに情報を提供できますか?
KeyValuePairをラッパークラスとして使用し、辞書を作成してセットを作成しますか?または、EqualsおよびGetHashCodeをオーバーライドする独自のラッパーを実装します。
Dictionary<KeyValuePair, bool> mySet;
for(int i = 0; i < keys.length; ++i)
{
KeyValuePair kvp = new KeyValuePair(keys[i], values[i]);
mySet[kvp] = true;
}
Dictionary
を使用する代わりに、 KeyedCollection<TKey, TItem>
を拡張しない理由a>?ドキュメントによると:
キーが値に埋め込まれているコレクションの抽象基本クラスを提供します。
次に、 protected TKey GetKeyForItem(TItem item)
関数をオーバーライドする必要があります。 IList<T>
と IDictionary<TKey, TValue>
かなり高速になる可能性が高いと思います。
方法:
Dictionary<NameValuePair,int> hs = new Dictionary<NameValuePair,int>();
foreach (i in jaggedArray)
{
foreach (j in i)
{
if (!hs.ContainsKey(j))
{
hs.Add(j, 0);
}
}
}
IEnumerable<NameValuePair> unique = hs.Keys;
もちろん、C#3.0、.NET 3.5を使用している場合:
var hs = new HashSet<NameValuePair>();
hs.UnionWith(jaggedArray.SelectMany(item => item));
トリックを実行します。
コードのプロファイルを作成しましたか? foreachループがボトルネックであり、retriever.GetVehicles()ではないことは確かです?
レトリーバーを偽造し、86.000 X 11の値を返す小さなテストプロジェクトを作成しました。最初の試行は5秒で実行され、含まれるデータが作成されました。
最初のキーが<!> quot; 0#0 <!> quotであるキーと値の両方に同じ値を使用しました。最後の<!> quot; 85999#10 <!> quot;。
その後、GUIDに切り替えました。同じ結果。
次に、このようにキーを長くしました:
var s = Guid.NewGuid().ToString();
return s + s + s + s + s + s + s+ s + s + s;
今ではほぼ10秒かかりました。
その後、キーをめちゃくちゃ長くして、メモリ不足例外を取得しました。コンピューターにスワップファイルがないため、すぐにこの例外が発生しました。
鍵の長さは?仮想メモリの消費がパフォーマンスの低下の原因ですか?