Có nhanh hơn không?
Xuất thân từ một góc độ gamedev, nếu chìa khóa của bạn là một loại giá trị (struct, nguyên thủy, enum, vv) cung cấp riêng EqualityComparer<T>
của bạn là đáng kể nhanh hơn - do thực tế các EqualityComparer<T>.Default
hộp giá trị.
Ví dụ thực tế, mẫu bảng quảng cáo Managed DirectX được sử dụng để chạy ở ~ 30% tốc độ của phiên bản C++; nơi tất cả các mẫu khác đang chạy ở ~ 90%. Lý do cho điều này là các biển quảng cáo đã được sắp xếp bằng cách sử dụng bộ so sánh mặc định (và do đó được đóng hộp), vì nó chỉ ra 4MB dữ liệu đã được sao chép xung quanh mỗi khung nhờ vào điều này.
Cách hoạt động?
Dictionary<K,V>
sẽ tự cung cấp EqualityComparer<T>.Default
cho chính nó thông qua hàm tạo mặc định. Có gì comparer bình đẳng mặc định làm là (về cơ bản, chú ý bao nhiêu đấm bốc xảy ra):
public void GetHashCode(T value)
{
return ((object)value).GetHashCode();
}
public void Equals(T first, T second)
{
return ((object)first).Equals((object)second);
}
Tại sao tôi sẽ không bao giờ sử dụng nó?
Đó là khá phổ biến để xem loại mã (khi cố gắng để có case-insensitive phím):
var dict = new Dictionary<string, int>();
dict.Add(myParam.ToUpperInvariant(), fooParam);
// ...
var val = dict[myParam.ToUpperInvariant()];
Đây thực sự là lãng phí, nó là tốt hơn để chỉ cần sử dụng một StringComparer trên constructor:
var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
Có nhanh hơn (redux) không?
Trong trường hợp cụ thể này, nó nhanh hơn rất nhiều, vì so sánh chuỗi thứ tự là loại so sánh chuỗi nhanh nhất bạn có thể thực hiện. Một điểm chuẩn nhanh:
static void Main(string[] args)
{
var d1 = new Dictionary<string, int>();
var d2 = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
d1.Add("FOO", 1);
d2.Add("FOO", 1);
Stopwatch s = new Stopwatch();
s.Start();
RunTest1(d1, "foo");
s.Stop();
Console.WriteLine("ToUpperInvariant: {0}", s.Elapsed);
s.Reset();
s.Start();
RunTest2(d2, "foo");
s.Stop();
Console.WriteLine("OrdinalIgnoreCase: {0}", s.Elapsed);
Console.ReadLine();
}
static void RunTest1(Dictionary<string, int> values, string val)
{
for (var i = 0; i < 10000000; i++)
{
values[val.ToUpperInvariant()] = values[val.ToUpperInvariant()];
}
}
static void RunTest2(Dictionary<string, int> values, string val)
{
for (var i = 0; i < 10000000; i++)
{
values[val] = values[val];
}
}
// ToUpperInvariant: 00:00:04.5084119
// OrdinalIgnoreCase: 00:00:02.1211549
// 2x faster.
Đặt
Có thể để loại bỏ các chi phí đấm bốc bằng cách thực hiện một giao diện trên một cấu trúc (như IEquatable<T>
). Tuy nhiên, có nhiều quy tắc đáng ngạc nhiên khi xảy ra sự kiện boxing trong những trường hợp này nên tôi khuyên bạn nên sử dụng giao diện được ghép nối (ví dụ: IEqualityComparer<T>
trong trường hợp này) nếu có thể.
Câu trả lời tuyệt vời, cảm ơn :) –
Câu trả lời hay nhưng tôi nghĩ bạn nên đề cập đến 'EqualityComparer .Default' trước tiên sẽ kiểm tra xem loại có thực hiện 'IEquatable ' và nếu có, sử dụng thực hiện; có nghĩa là bạn không _have to_ cung cấp một so sánh tùy chỉnh chỉ để tránh boxing nếu loại giá trị của bạn thực hiện giao diện 'IEquatable '. –
@ ŞafakGür sử dụng giao diện để truy cập các loại giá trị sẽ hộp cho chúng: http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp –