Điều gì xảy ra với tra cứu C # Dictionary <int, int> nếu khóa không tồn tại?


121

Tôi đã thử kiểm tra null nhưng trình biên dịch cảnh báo rằng điều kiện này sẽ không bao giờ xảy ra. Tôi nên tìm kiếm gì?

Câu trả lời:


196

Giả sử bạn muốn để có được những giá trị nếu chìa khóa không tồn tại, sử dụng Dictionary<TKey, TValue>.TryGetValue:

int value;
if (dictionary.TryGetValue(key, out value))
{
    // Key was in dictionary; "value" contains corresponding value
} 
else 
{
    // Key wasn't in dictionary; "value" is now 0
}

(Sử dụng ContainsKeyvà sau đó trình lập chỉ mục làm cho nó nhìn chìa khóa lên hai lần, điều này khá vô nghĩa.)

Lưu ý rằng ngay cả khi bạn đang sử dụng các kiểu tham chiếu, việc kiểm tra null sẽ không hoạt động - trình lập chỉ mục cho Dictionary<,>sẽ đưa ra một ngoại lệ nếu bạn yêu cầu một khóa bị thiếu, thay vì trả về null. (Đây là sự khác biệt lớn giữa Dictionary<,>Hashtable.)


@JonSkeet Không phải TryGetValue cũng đang thực hiện tra cứu hai lần ( như đã nêu trong phần câu hỏi này )?
nawfal

5
@nawfal: Tôi không thấy có dấu hiệu nào cho thấy câu hỏi đó nói lên điều đó cả. Nó nói rằng nó đang làm nhiều việc hơn ContainsKey, điều đó đúng, bởi vì nó cũng phải trích xuất giá trị. Nó không thực hiện hai lần tra cứu.
Jon Skeet

Ngây thơ, tôi cứ mong đợi là null, nhưng đối với Dictionary <TKey, enum>, điều này trả về giá trị tương đương "0" trong enum.
Jess

23

Từ điển ném ra một KeyNotFoundngoại lệ trong trường hợp từ điển không chứa khóa của bạn.

Theo đề nghị, ContainsKeylà biện pháp phòng ngừa thích hợp. TryGetValuecũng có hiệu quả.

Điều này cho phép từ điển lưu trữ giá trị null hiệu quả hơn. Nếu không nó hoạt động theo cách này, việc kiểm tra kết quả null từ toán tử [] sẽ chỉ ra giá trị null HOẶC sự không tồn tại của khóa đầu vào là không tốt.


Thông tin bổ sung có thể được tìm thấy tại MSDN: msdn.microsoft.com/en-gb/library/9tee9ht2.aspx
cyberzed

10

Nếu bạn chỉ đang kiểm tra trước khi cố gắng thêm một giá trị mới, hãy sử dụng ContainsKeyphương pháp:

if (!openWith.ContainsKey("ht"))
{
    openWith.Add("ht", "hypertrm.exe");
}

Nếu bạn đang kiểm tra xem giá trị có tồn tại hay không, hãy sử dụng TryGetValuephương pháp như được mô tả trong câu trả lời của Jon Skeet.


8
TryGet là tốt hơn
Ruben Bartelink

2
Có thể là bạn đang giải quyết việc tra cứu khóa thông qua bảng băm hai lần nếu bạn ngay lập tức Nhận sau Chứa. Wintellect PowerCollections cũng có GetValueElseAddcác phương thức mà bạn cung cấp giá trị (hoặc a Func<TValue>) để lưu độ phân giải trên Chèn nếu bạn định thêm nếu nó không có ở đó. Tôi đoán lý do mà has not đã làm cho nó vào libs NET là vì Add đường dẫn là ít thường xuyên nếu bạn đang sử dụng nó trong một bộ nhớ cache stylee]
Ruben Bartelink

@rub: Tôi đoán điều đó phụ thuộc vào mục đích của mã. Nếu bạn muốn sử dụng giá trị mà tôi đồng ý TryGetValuesẽ tốt hơn, nhưng nếu bạn muốn kiểm tra xem từ điển có chứa khóa hay không để tránh bổ sung trùng lặp, tôi sẽ nói ContainsKeylà tốt (nếu không tốt hơn).
Fredrik Mörk

@Fredrik: Nếu bạn chỉ muốn kiểm tra khả năng ngăn chặn, thì có, bạn nên sử dụng ContainsKey. Lưu ý rằng đó không phải là trường hợp trong mã mẫu của câu trả lời này.
Jon Skeet

@Jon: true, tôi thực sự đã bỏ lỡ rằng giá trị gia tăng đã được tìm nạp ngay sau khi nó được thêm vào.
Fredrik Mörk

3

Bạn nên kiểm tra Dictionary.ContainsKey (khóa int) trước khi cố gắng lấy ra giá trị.

Dictionary<int, int> myDictionary = new Dictionary<int, int>();
myDictionary.Add(2,4);
myDictionary.Add(3,5);

int keyToFind = 7;
if(myDictionary.ContainsKey(keyToFind))
{
    myValueLookup = myDictionay[keyToFind];
    // do work...
}
else
{
    // the key doesn't exist.
}

2
Tại sao bạn muốn làm cho nó thực hiện tra cứu hai lần?
Jon Skeet

2
@mookid: Theo tôi thì không. Ý tưởng là cố gắng tìm kiếm chìa khóa và thực hiện một hành động nếu nó được tìm thấy, và một hướng hành động khác, phải không?
Jon Skeet

3
@Jon - Thành thật mà nói? Bởi vì tôi không biết về TryGetValue. Rất may, tôi làm ngay bây giờ, vì vậy tôi sẽ biết trong tương lai. Tôi sẽ để nguyên câu trả lời này, mặc dù cuộc thảo luận 'cos rất có giá trị.
ZombieSheep

@Jon Skeet - Đó là lý do tại sao tôi ở đây. :)
ZombieSheep

@JonSkeet Vì trước C # 7, bạn không thể sử dụng TryGetValuetrong biểu thức lambda. Mặc dù điều đó khiến tôi nghĩ rằng một phần mở rộng mới cho C # sẽ là một catchtoán tử tương tự như toán tử liên nullkết.
NetMage

1

Một lớp trợ giúp rất hữu ích:

public static class DictionaryHelper
{
    public static TVal Get<TKey, TVal>(this Dictionary<TKey, TVal> dictionary, TKey key, TVal defaultVal = default(TVal))
    {
        TVal val;
        if( dictionary.TryGetValue(key, out val) )
        {
            return val;
        }
        return defaultVal;
    }
}

Đôi khi tôi tự hỏi tại sao điều này không được thêm vào thư viện tiêu chuẩn. Gần như tất cả các ngôn ngữ sử dụng hashmaps đều trả về null nếu không có mục nhập, không phải là một ngoại lệ đáng sợ. Một mục không tồn tại trong từ điển của bạn không phải là hành vi đặc biệt.
Adam Hess

@AdamHess - đó là lý do tại sao bạn có Hashtable () trong c # ... không may, các phím của bạn có được đóng hộp có ... :(
veljkoz


0

Bạn có thể nên sử dụng:

if(myDictionary.ContainsKey(someInt))
{
  // do something
}

Lý do tại sao bạn không thể kiểm tra null là vì khóa ở đây là một loại giá trị.


1
Loại giá trị hơi không liên quan, vì việc kiểm tra null sẽ không có hiệu quả mong muốn.
Jon Skeet

@Johannes, giải pháp của Jon tất nhiên là tốt hơn, nhưng người hỏi đã nói rằng anh ta đã kiểm tra xem khóa có tồn tại hay không và nó là một Từ điển <int, int>, vì vậy khóa cũng là một kiểu giá trị ở đây.
Razzie

0
int result= YourDictionaryName.TryGetValue(key, out int value) ? YourDictionaryName[key] : 0;

Nếu khóa có trong từ điển, nó trả về giá trị của khóa, ngược lại nó trả về 0.

Hy vọng, mã này sẽ giúp bạn.


1
Nếu khóa tồn tại, mã này sẽ tra cứu hai lần. TryGetValuelà đủ, sử dụng valuethay vìresult
Mathieu VIALES

0

Hãy xem xét tùy chọn đóng gói từ điển cụ thể này và cung cấp một phương thức để trả về giá trị cho khóa đó:

public static class NumbersAdapter
{
    private static readonly Dictionary<string, string> Mapping = new Dictionary<string, string>
    {
        ["1"] = "One",
        ["2"] = "Two",
        ["3"] = "Three"
    };

    public static string GetValue(string key)
    {
        return Mapping.ContainsKey(key) ? Mapping[key] : key;
    }
}

Sau đó, bạn có thể quản lý hành vi của từ điển này.

Ví dụ ở đây: nếu từ điển không có khóa, nó sẽ trả về khóa mà bạn truyền theo tham số.

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.