Điều gì hiệu quả hơn: Từ điển TryGetValue hoặc ContainsKey + Item?


251

Từ mục nhập của MSDN trên Dictionary.TryGetValue Phương thức :

Phương thức này kết hợp chức năng của phương thức ContainsKey và thuộc tính Item.

Nếu không tìm thấy khóa, thì tham số giá trị sẽ nhận giá trị mặc định phù hợp cho loại giá trị TValue; ví dụ: 0 (không) cho các loại số nguyên, sai cho các loại Boolean và null cho các loại tham chiếu.

Sử dụng phương thức TryGetValue nếu mã của bạn thường xuyên cố gắng truy cập các khóa không có trong từ điển. Sử dụng phương pháp này hiệu quả hơn so với việc bắt KeyNotFoundException được ném bởi thuộc tính Item.

Phương pháp này tiếp cận một hoạt động O (1).

Từ mô tả, không rõ liệu nó hiệu quả hơn hay chỉ thuận tiện hơn so với việc gọi ContainsKey và sau đó thực hiện tra cứu. Có phải việc triển khai TryGetValuechỉ gọi ContainsKey và sau đó là Item hoặc thực sự hiệu quả hơn thế bằng cách thực hiện một tra cứu?

Nói cách khác, cái gì hiệu quả hơn (nghĩa là cái nào thực hiện ít tra cứu hơn):

Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
  ival = dict[ikey];
}
else
{
  ival = default(int);
}

hoặc là

Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);

Lưu ý: Tôi không tìm kiếm một điểm chuẩn!

Câu trả lời:


313

TryGetValue sẽ nhanh hơn

ContainsKeysử dụng cùng một kiểm tra như TryGetValue, trong đó đề cập đến nội bộ vị trí nhập thực tế. Các Itembất động sản thực sự có chức năng đang gần giống như TryGetValue, ngoại trừ việc nó sẽ ném một ngoại lệ thay vì trả lại sai.

Sử dụng ContainsKeytheo sau là Itemsao chép cơ bản chức năng tra cứu, phần lớn tính toán trong trường hợp này.


2
Điều này là tinh tế hơn : if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);. Nhưng tôi nghĩ rằng TryGetValuevẫn còn hiệu quả hơn kể từ khi nhận set thuộc tính của bộ chỉ mục được sử dụng, phải không?
Tim Schmelter

4
bây giờ bạn thực sự có thể xem nguồn .net cho nó: referencesource.microsoft.com/#mscorlib/system/collections/... bạn có thể thấy rằng tất cả 3 TryGetValue, containsKey, và điều này [] gọi phương thức FindEntry cùng và làm cùng một lượng công việc, chỉ khác nhau về cách họ trả lời câu hỏi: trygetvalue trả về bool và giá trị, chứa khóa chỉ trả về true / false và [] này trả về giá trị hoặc ném ngoại lệ.
John Gardner

1
@JohnGardner Vâng, đó là những gì tôi đã nói - nhưng nếu bạn làm ContainsKey thì hãy lấy Item, bạn đang làm công việc đó gấp 2 lần thay vì 1x.
Sậy Copsey

3
tôi hoàn toàn đồng ý :) tôi chỉ chỉ ra rằng nguồn thực tế hiện có sẵn. không có câu trả lời nào khác / v.v có liên kết đến nguồn thực tế: D
John Gardner

1
Hơi lạc đề, nếu bạn truy cập qua IDadata trong môi trường đa luồng, tôi sẽ luôn sử dụng TryGetValue vì trạng thái có thể thay đổi từ khi bạn gọi ContainsKey (không có gì đảm bảo rằng TryGetValue sẽ khóa chính xác, nhưng có lẽ sẽ an toàn hơn)
Chris Berry

91

Một điểm chuẩn nhanh cho thấy TryGetValuecó một lợi thế nhỏ:

    static void Main() {
        var d = new Dictionary<string, string> {{"a", "b"}};
        var start = DateTime.Now;
        for (int i = 0; i != 10000000; i++) {
            string x;
            if (!d.TryGetValue("a", out x)) throw new ApplicationException("Oops");
            if (d.TryGetValue("b", out x)) throw new ApplicationException("Oops");
        }
        Console.WriteLine(DateTime.Now-start);
        start = DateTime.Now;
        for (int i = 0; i != 10000000; i++) {
            string x;
            if (d.ContainsKey("a")) {
                x = d["a"];
            } else {
                x = default(string);
            }
            if (d.ContainsKey("b")) {
                x = d["b"];
            } else {
                x = default(string);
            }
        }
   }

Điều này tạo ra

00:00:00.7600000
00:00:01.0610000

làm cho việc ContainsKey + Itemtruy cập chậm hơn khoảng 40% với giả định là sự pha trộn giữa các lượt truy cập và lượt bỏ lỡ.

Hơn nữa, khi tôi thay đổi chương trình để luôn bỏ lỡ (tức là luôn luôn tìm kiếm "b"), hai phiên bản trở nên nhanh như nhau:

00:00:00.2850000
00:00:00.2720000

Tuy nhiên, khi tôi thực hiện "tất cả các lần truy cập", thì TryGetValuevẫn là một người chiến thắng rõ ràng:

00:00:00.4930000
00:00:00.8110000

11
Tất nhiên, nó phụ thuộc vào mô hình sử dụng thực tế. Nếu bạn gần như không bao giờ thất bại trong việc tra cứu thì TryGetValuenên tiến xa. Ngoài ra ... một nitpick ... DateTimekhông phải là cách tốt nhất để ghi lại các phép đo hiệu suất.
Ed S.

4
@ Chỉnh sửa. Bạn đã đúng, TryGetValuethậm chí còn dẫn đầu. Tôi đã chỉnh sửa câu trả lời để bao gồm một kịch bản "tất cả các lần truy cập" và "tất cả bỏ lỡ".
dasblinkenlight

2
@Luciano giải thích cách bạn sử dụng Any- Như thế này : Any(i=>i.Key==key). Trong trường hợp, vâng, đó là một tìm kiếm tuyến tính xấu của từ điển.
weston

13
DateTime.Nowsẽ chỉ chính xác đến một vài ms. Sử dụng các Stopwatchlớp trong System.Diagnosticsđể thay thế (trong đó sử dụng QueryPerformanceCounter dưới tấm chăn để cung cấp chính xác cao hơn nhiều). Nó cũng dễ sử dụng hơn.
Alastair Maw

5
Ngoài nhận xét của Alastair và Ed - DateTime. Bây giờ có thể quay ngược lại, nếu bạn nhận được cập nhật thời gian, chẳng hạn như xảy ra khi người dùng cập nhật thời gian trên máy tính của họ, múi giờ bị vượt qua hoặc múi giờ thay đổi (DST, cho ví dụ). Hãy thử làm việc trên một hệ thống có đồng hồ hệ thống được đồng bộ hóa theo thời gian được cung cấp bởi một số dịch vụ radio như GPS hoặc mạng điện thoại di động. DateTime.Now sẽ đi khắp mọi nơi và DateTime.UtcNow chỉ khắc phục một trong những nguyên nhân đó. Chỉ cần sử dụng Đồng hồ bấm giờ.
antiduh

51

Vì không có câu trả lời nào thực sự trả lời được câu hỏi, đây là câu trả lời chấp nhận được tôi tìm thấy sau một số nghiên cứu:

Nếu bạn dịch ngược TryGetValue, bạn sẽ thấy rằng nó đang làm điều này:

public bool TryGetValue(TKey key, out TValue value)
{
  int index = this.FindEntry(key);
  if (index >= 0)
  {
    value = this.entries[index].value;
    return true;
  }
  value = default(TValue);
  return false;
}

trong khi phương thức ContainsKey là:

public bool ContainsKey(TKey key)
{
  return (this.FindEntry(key) >= 0);
}

vì vậy TryGetValue chỉ là ContainsKey cộng với tra cứu mảng nếu có mặt hàng.

Nguồn

Dường như TryGetValue sẽ nhanh gần gấp đôi so với kết hợp ContainsKey + Item.


20

Ai quan tâm :-)

Có lẽ bạn đang hỏi vì TryGetValuesử dụng một nỗi đau - vì vậy hãy gói gọn nó như thế này bằng một phương pháp mở rộng.

public static class CollectionUtils
{
    // my original method
    // public static V GetValueOrDefault<K, V>(this Dictionary<K, V> dic, K key)
    // {
    //    V ret;
    //    bool found = dic.TryGetValue(key, out ret);
    //    if (found)
    //    {
    //        return ret;
    //    }
    //    return default(V);
    // }


    // EDIT: one of many possible improved versions
    public static TValue GetValueOrDefault<K, V>(this IDictionary<K, V> dictionary, K key)
    {
        // initialized to default value (such as 0 or null depending upon type of TValue)
        TValue value;  

        // attempt to get the value of the key from the dictionary
        dictionary.TryGetValue(key, out value);
        return value;
    }

Sau đó chỉ cần gọi:

dict.GetValueOrDefault("keyname")

hoặc là

(dict.GetValueOrDefault("keyname") ?? fallbackValue) 

1
@ Hüseyin Tôi đã rất bối rối làm thế nào tôi đủ ngu ngốc để đăng bài này mà không có thisnhưng tôi có phương pháp của mình được nhân đôi hai lần trong cơ sở mã của mình - một lần và một mà không có thislý do tại sao tôi không bao giờ bắt được nó! cảm ơn đã sửa chữa
Simon_Weaver

2
TryGetValuegán giá trị mặc định cho tham số giá trị out nếu khóa không tồn tại, vì vậy điều này có thể được đơn giản hóa.
Raphael Smit

2
Phiên bản đơn giản hóa: TValue tĩnh công khai GetValueOrDefault <TKey, TValue> (Từ điển này <TKey, TValue> dict, phím TKey) {TValue ret; dict.TryGetValue (key, out ret); trở về ret; }
Joshua

2
Trong C # 7, điều này thực sự thú vị:if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
Shimmy Weitzhandler

1
Trớ trêu thay, mã nguồn thực đã có một thói quen GetValueOrDefault (), nhưng nó ẩn ... referencesource.microsoft.com/#mscorlib/system/collections/...
Deven T. Corzine

10

Tại sao bạn không kiểm tra nó?

Nhưng tôi khá chắc chắn rằng TryGetValuenó nhanh hơn, bởi vì nó chỉ thực hiện một lần tra cứu. Tất nhiên điều này không được đảm bảo, tức là các triển khai khác nhau có thể có các đặc tính hiệu suất khác nhau.

Cách tôi thực hiện một từ điển là bằng cách tạo một Findhàm bên trong để tìm vị trí cho một mục, sau đó xây dựng phần còn lại trên đó.


Tôi không nghĩ rằng các chi tiết triển khai có thể có thể thay đổi đảm bảo rằng thực hiện hành động X một lần nhanh hơn hoặc bằng thực hiện hành động X hai lần. Trường hợp tốt nhất là giống hệt nhau, trường hợp xấu hơn phiên bản 2X mất gấp đôi thời gian.
Dan Bechard

9

Tất cả các câu trả lời cho đến nay, mặc dù tốt, bỏ lỡ một điểm quan trọng.

Các phương thức vào các lớp của API (ví dụ: .NET framework) là một phần của định nghĩa giao diện (không phải giao diện C # hoặc VB, mà là giao diện theo nghĩa khoa học máy tính).

Như vậy, thường không chính xác khi hỏi liệu gọi phương thức như vậy có nhanh hơn hay không, trừ khi tốc độ là một phần của định nghĩa giao diện chính thức (điều này không có trong trường hợp này).

Theo truyền thống, loại phím tắt này (kết hợp tìm kiếm và truy xuất) hiệu quả hơn bất kể ngôn ngữ, cơ sở hạ tầng, hệ điều hành, nền tảng hoặc kiến ​​trúc máy. Nó cũng dễ đọc hơn, bởi vì nó thể hiện rõ ràng ý định của bạn, thay vì ngụ ý nó (từ cấu trúc mã của bạn).

Vì vậy, câu trả lời (từ một bản hack cũ nát) chắc chắn là 'Có' (TryGetValue thích hợp hơn là kết hợp giữa ContainsKey và Mục [Nhận] để lấy giá trị từ Từ điển).

Nếu bạn nghĩ điều này nghe có vẻ kỳ quặc, hãy nghĩ về nó như thế này: Ngay cả khi các triển khai hiện tại của TryGetValue, ContainsKey và Item [Get] không mang lại bất kỳ sự khác biệt về tốc độ nào, bạn có thể cho rằng có khả năng đó là một triển khai trong tương lai (ví dụ .NET v5) sẽ làm (TryGetValue sẽ nhanh hơn). Hãy suy nghĩ về tuổi thọ của phần mềm của bạn.

Bên cạnh đó, thật thú vị khi lưu ý rằng các công nghệ định nghĩa giao diện hiện đại điển hình vẫn hiếm khi cung cấp bất kỳ phương tiện nào để xác định chính thức các ràng buộc thời gian. Có lẽ .NET v5?


2
Mặc dù tôi 100% đồng ý với lập luận của bạn về ngữ nghĩa, nhưng vẫn đáng để thực hiện bài kiểm tra hiệu suất. Bạn không bao giờ biết khi nào API bạn đang sử dụng có triển khai dưới mức tối ưu sao cho điều đúng về mặt ngữ nghĩa xảy ra chậm hơn, trừ khi bạn thực hiện kiểm tra.
Dan Bechard

5

Thực hiện một chương trình thử nghiệm nhanh, chắc chắn có một sự cải tiến bằng cách sử dụng TryGetValue với 1 triệu mục trong từ điển.

Các kết quả:

Chứa khóa + Mục cho 1000000 lượt truy cập: 45ms

TryGetValue cho 1000000 lượt truy cập: 26ms

Đây là ứng dụng thử nghiệm:

static void Main(string[] args)
{
    const int size = 1000000;

    var dict = new Dictionary<int, string>();

    for (int i = 0; i < size; i++)
    {
        dict.Add(i, i.ToString());
    }

    var sw = new Stopwatch();
    string result;

    sw.Start();

    for (int i = 0; i < size; i++)
    {
        if (dict.ContainsKey(i))
            result = dict[i];
    }

    sw.Stop();
    Console.WriteLine("ContainsKey + Item for {0} hits: {1}ms", size, sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();

    for (int i = 0; i < size; i++)
    {
        dict.TryGetValue(i, out result);
    }

    sw.Stop();
    Console.WriteLine("TryGetValue for {0} hits: {1}ms", size, sw.ElapsedMilliseconds);

}

5

Trên máy của tôi, với vô số RAM, khi chạy ở chế độ ĐÁNG TIN CẬY (không phải DEBUG), ContainsKeybằng TryGetValue/ try-catchnếu tất cả các mục trong mục Dictionary<>được tìm thấy.

ContainsKeyvượt trội hơn tất cả khi chỉ có một vài mục từ điển không tìm thấy (trong ví dụ của tôi dưới đây, được đặt MAXVALthành bất kỳ thứ gì lớn hơn ENTRIESđể có một số mục bị bỏ lỡ):

Các kết quả:

Finished evaluation .... Time distribution:
Size: 000010: TryGetValue: 53,24%, ContainsKey: 1,74%, try-catch: 45,01% - Total: 2.006,00
Size: 000020: TryGetValue: 37,66%, ContainsKey: 0,53%, try-catch: 61,81% - Total: 2.443,00
Size: 000040: TryGetValue: 22,02%, ContainsKey: 0,73%, try-catch: 77,25% - Total: 7.147,00
Size: 000080: TryGetValue: 31,46%, ContainsKey: 0,42%, try-catch: 68,12% - Total: 17.793,00
Size: 000160: TryGetValue: 33,66%, ContainsKey: 0,37%, try-catch: 65,97% - Total: 36.840,00
Size: 000320: TryGetValue: 34,53%, ContainsKey: 0,39%, try-catch: 65,09% - Total: 71.059,00
Size: 000640: TryGetValue: 32,91%, ContainsKey: 0,32%, try-catch: 66,77% - Total: 141.789,00
Size: 001280: TryGetValue: 39,02%, ContainsKey: 0,35%, try-catch: 60,64% - Total: 244.657,00
Size: 002560: TryGetValue: 35,48%, ContainsKey: 0,19%, try-catch: 64,33% - Total: 420.121,00
Size: 005120: TryGetValue: 43,41%, ContainsKey: 0,24%, try-catch: 56,34% - Total: 625.969,00
Size: 010240: TryGetValue: 29,64%, ContainsKey: 0,61%, try-catch: 69,75% - Total: 1.197.242,00
Size: 020480: TryGetValue: 35,14%, ContainsKey: 0,53%, try-catch: 64,33% - Total: 2.405.821,00
Size: 040960: TryGetValue: 37,28%, ContainsKey: 0,24%, try-catch: 62,48% - Total: 4.200.839,00
Size: 081920: TryGetValue: 29,68%, ContainsKey: 0,54%, try-catch: 69,77% - Total: 8.980.230,00

Đây là mã của tôi:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                const int ENTRIES = 10000, MAXVAL = 15000, TRIALS = 100000, MULTIPLIER = 2;
                Dictionary<int, int> values = new Dictionary<int, int>();
                Random r = new Random();
                int[] lookups = new int[TRIALS];
                int val;
                List<Tuple<long, long, long>> durations = new List<Tuple<long, long, long>>(8);

                for (int i = 0;i < ENTRIES;++i) try
                    {
                        values.Add(r.Next(MAXVAL), r.Next());
                    }
                    catch { --i; }

                for (int i = 0;i < TRIALS;++i) lookups[i] = r.Next(MAXVAL);

                Stopwatch sw = new Stopwatch();
                ConsoleColor bu = Console.ForegroundColor;

                for (int size = 10;size <= TRIALS;size *= MULTIPLIER)
                {
                    long a, b, c;

                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Loop size: {0}", size);
                    Console.ForegroundColor = bu;

                    // ---------------------------------------------------------------------
                    sw.Start();
                    for (int i = 0;i < size;++i) values.TryGetValue(lookups[i], out val);
                    sw.Stop();
                    Console.WriteLine("TryGetValue: {0}", a = sw.ElapsedTicks);

                    // ---------------------------------------------------------------------
                    sw.Restart();
                    for (int i = 0;i < size;++i) val = values.ContainsKey(lookups[i]) ? values[lookups[i]] : default(int);
                    sw.Stop();
                    Console.WriteLine("ContainsKey: {0}", b = sw.ElapsedTicks);

                    // ---------------------------------------------------------------------
                    sw.Restart();
                    for (int i = 0;i < size;++i)
                        try { val = values[lookups[i]]; }
                        catch { }
                    sw.Stop();
                    Console.WriteLine("try-catch: {0}", c = sw.ElapsedTicks);

                    // ---------------------------------------------------------------------
                    Console.WriteLine();

                    durations.Add(new Tuple<long, long, long>(a, b, c));
                }

                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Finished evaluation .... Time distribution:");
                Console.ForegroundColor = bu;

                val = 10;
                foreach (Tuple<long, long, long> d in durations)
                {
                    long sum = d.Item1 + d.Item2 + d.Item3;

                    Console.WriteLine("Size: {0:D6}:", val);
                    Console.WriteLine("TryGetValue: {0:P2}, ContainsKey: {1:P2}, try-catch: {2:P2} - Total: {3:N}", (decimal)d.Item1 / sum, (decimal)d.Item2 / sum, (decimal)d.Item3 / sum, sum);
                    val *= MULTIPLIER;
                }

                Console.WriteLine();
            }
        }
    }

Tôi cảm thấy như có gì đó tanh đang diễn ra ở đây. Tôi tự hỏi nếu trình tối ưu hóa có thể xóa hoặc đơn giản hóa các kiểm tra ContainsKey () của bạn do thực tế là bạn không bao giờ sử dụng giá trị được truy xuất.
Dan Bechard

Nó chỉ là không thể. ContainsKey () nằm trong một DLL được biên dịch. Trình tối ưu hóa không biết gì về những gì ContainsKey () thực sự làm. Nó có thể gây ra tác dụng phụ, vì vậy nó phải được gọi và không thể rút ngắn.
AxD

Một cái gì đó không có thật ở đây. Thực tế là việc kiểm tra mã .NET cho thấy ContainsKey, TryGetValue và [] tất cả đều gọi cùng một mã nội bộ, do đó, TryGetValue nhanh hơn ContainsKey + này [] khi mục nhập tồn tại.
Jim Balter

3

Ngoài việc thiết kế một microbenchmark sẽ cho kết quả chính xác trong một thiết lập thực tế, bạn có thể kiểm tra nguồn tham chiếu của .NET Framework.

Tất cả đều gọi FindEntry(TKey)phương thức thực hiện hầu hết công việc và không ghi nhớ kết quả của nó, vì vậy việc gọi TryGetValuenhanh gần gấp đôi so với ContainsKey+Item .


Giao diện bất tiện TryGetValuecó thể được điều chỉnh bằng phương pháp mở rộng :

using System.Collections.Generic;

namespace Project.Common.Extensions
{
    public static class DictionaryExtensions
    {
        public static TValue GetValueOrDefault<TKey, TValue>(
            this IDictionary<TKey, TValue> dictionary,
            TKey key,
            TValue defaultValue = default(TValue))
        {
            if (dictionary.TryGetValue(key, out TValue value))
            {
                return value;
            }
            return defaultValue;
        }
    }
}

Kể từ C # 7.1, bạn có thể thay thế default(TValue)bằng trơn default. Các loại được suy ra.

Sử dụng:

var dict = new Dictionary<string, string>();
string val = dict.GetValueOrDefault("theKey", "value used if theKey is not found in dict");

Nó trả về nullcác kiểu tham chiếu có tra cứu thất bại, trừ khi giá trị mặc định rõ ràng được chỉ định.

var dictObj = new Dictionary<string, object>();
object valObj = dictObj.GetValueOrDefault("nonexistent");
Debug.Assert(valObj == null);

val dictInt = new Dictionary<string, int>();
int valInt = dictInt.GetValueOrDefault("nonexistent");
Debug.Assert(valInt == 0);

Lưu ý rằng người dùng phương thức tiện ích mở rộng không thể cho biết sự khác biệt giữa khóa không tồn tại và khóa tồn tại nhưng giá trị của nó là mặc định (T).
Lucas

Trên một máy tính hiện đại, nếu bạn gọi một chương trình con hai lần liên tiếp, sẽ khó có thể mất gấp đôi thời gian gọi nó một lần. Điều này là do kiến ​​trúc CPU và bộ nhớ đệm rất có khả năng lưu trữ rất nhiều hướng dẫn và dữ liệu liên quan đến cuộc gọi đầu tiên, vì vậy cuộc gọi thứ hai sẽ được thực hiện nhanh hơn. Mặt khác, gọi hai lần gần như chắc chắn sẽ mất nhiều thời gian hơn so với gọi một lần, do đó, vẫn có một lợi thế để loại bỏ cuộc gọi thứ hai nếu có thể.
người tranh luận

2

Nếu bạn đang cố gắng lấy ra giá trị từ từ điển, thì TryGetValue (khóa, giá trị ngoài) là lựa chọn tốt nhất, nhưng nếu bạn đang kiểm tra sự hiện diện của khóa, để chèn mới, không ghi đè các khóa cũ, thì sẽ ghi đè các khóa cũ, và chỉ với phạm vi đó, ContainsKey (khóa) là tùy chọn tốt nhất, điểm chuẩn có thể xác nhận điều này:

using System;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;

namespace benchmark
{
class Program
{
    public static Random m_Rand = new Random();
    public static Dictionary<int, int> testdict = new Dictionary<int, int>();
    public static Hashtable testhash = new Hashtable();

    public static void Main(string[] args)
    {
        Console.WriteLine("Adding elements into hashtable...");
        Stopwatch watch = Stopwatch.StartNew();
        for(int i=0; i<1000000; i++)
            testhash[i]=m_Rand.Next();
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- pause....", watch.Elapsed.TotalSeconds);
        Thread.Sleep(4000);
        Console.WriteLine("Adding elements into dictionary...");
        watch = Stopwatch.StartNew();
        for(int i=0; i<1000000; i++)
            testdict[i]=m_Rand.Next();
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- pause....", watch.Elapsed.TotalSeconds);
        Thread.Sleep(4000);

        Console.WriteLine("Finding the first free number for insertion");
        Console.WriteLine("First method: ContainsKey");
        watch = Stopwatch.StartNew();
        int intero=0;
        while (testdict.ContainsKey(intero))
        {
            intero++;
        }
        testdict.Add(intero, m_Rand.Next());
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- added value {1} in dictionary -- pause....", watch.Elapsed.TotalSeconds, intero);
        Thread.Sleep(4000);
        Console.WriteLine("Second method: TryGetValue");
        watch = Stopwatch.StartNew();
        intero=0;
        int result=0;
        while(testdict.TryGetValue(intero, out result))
        {
            intero++;
        }
        testdict.Add(intero, m_Rand.Next());
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- added value {1} in dictionary -- pause....", watch.Elapsed.TotalSeconds, intero);
        Thread.Sleep(4000);
        Console.WriteLine("Test hashtable");
        watch = Stopwatch.StartNew();
        intero=0;
        while(testhash.Contains(intero))
        {
            intero++;
        }
        testhash.Add(intero, m_Rand.Next());
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- added value {1} into hashtable -- pause....", watch.Elapsed.TotalSeconds, intero);
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }
}
}

Đây là một ví dụ đúng, tôi có một dịch vụ mà với mỗi "Mục" được tạo, nó liên kết một số lũy tiến, số này, mỗi khi bạn tạo một mục mới, phải được tìm thấy miễn phí, nếu bạn xóa một Mục, số miễn phí sẽ trở thành miễn phí, tất nhiên điều này không được tối ưu hóa, vì tôi có một var tĩnh lưu trữ số hiện tại, nhưng trong trường hợp bạn kết thúc tất cả các số, bạn có thể bắt đầu lại từ 0 đến UInt32.MaxValue

Kiểm tra thực hiện:
Thêm các yếu tố vào hashtable ...
Thực hiện trong 0,5908 - tạm dừng ....
Thêm các phần tử vào từ điển ...
Thực hiện trong 0,2679 - tạm dừng ....
Tìm số miễn phí đầu tiên để chèn
Phương thức đầu tiên : ContainsKey
Xong trong 0,0561 - giá trị gia tăng 1000000 trong từ điển - tạm dừng ....
Phương pháp thứ hai: TryGetValue
Thực hiện trong 0,0643 - giá trị gia tăng 1000001 trong từ điển - tạm dừng ....
Kiểm tra hashtable
Thực hiện trong 0, 3015 - giá trị gia tăng 1000000 vào hashtable - tạm dừng ....
Nhấn phím bất kỳ để tiếp tục. .

Nếu một số bạn có thể hỏi liệu ContainsKeys có lợi thế hay không, tôi thậm chí đã thử đảo ngược khóa TryGetValue bằng khóa Chứa, kết quả là như nhau.

Vì vậy, đối với tôi, với sự cân nhắc cuối cùng, tất cả phụ thuộc vào cách ứng xử của chương trình.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.