Dictionary<TKey, TValue> değerleri benzersiz anahtarlar altında saklar ve kaç girdi olursa olsun bir değeri anahtarıyla kabaca sabit zamanda bulur. Bir hash map'in C# karşılığıdır: addan numaraya bir telefon rehberi, ID'den kayda bir önbellek, kelime başına bir sayı.
Bir dictionary oluşturmak ve değer okumak
Çıktı:
12
2.50
2
True
False
İki başlatıcı biçimi de aynı işi yapar. ["key"] = value biçimi (C# 6) indeksleyiciyi kullanır, bu yüzden tekrarlanan bir anahtar üzerine yazar; { key, value } biçimi Add'i çağırır, bu yüzden tekrarlanan bir anahtar satır çalıştığında istisna fırlatır.
ContainsKey bir hash aramasıdır ve hızlıdır. ContainsValue ise her girdiyi taramak zorundadır, çünkü değerler indekslenmez.
Add, indeksleyici ve TryAdd
Bir girdi koymanın üç yolu vardır ve yalnızca anahtar zaten varken ne olduğunda farklılaşırlar:
Çıktı:
26
Caught ArgumentException
True
False
31
Add'in tekrarda istisna fırlatması bir özelliktir: benzersiz olması gerektiği halde olmayan veriyi ortaya çıkarır. Kastettiğiniz "ekle ya da güncelle" ise indeksleyiciyi, ilk değerin kazanması gerekiyorsa TryAdd'i (.NET Core 2.0 ve sonrası) kullanın.
Anahtarlar null olamaz. Add(null, ...) ya da dict[null] ArgumentNullException fırlatır. Değer tipi izin veriyorsa değerler null olabilir.
KeyNotFoundException ve TryGetValue
Var olmayan bir anahtarı indeksleyiciyle okumak KeyNotFoundException fırlatır. Bu en yaygın dictionary hatasıdır ve çözümü neredeyse her zaman TryGetValue'dur.
Çıktı:
Caught KeyNotFoundException
Found ana@example.com
Missing, value is null: True
no email
TryGetValue tek bir hash araması yapar ve başarıyı bir bool olarak bildirir. if (dict.ContainsKey(k)) { var v = dict[k]; } kalıbı çalışır ama anahtarı iki kez arar. Anahtar yoksa out değişkeni tipin varsayılan değerini alır (null, 0, false).
.NET Core 2.0 ve sonrasında, anahtar yoksa yedeği döndüren GetValueOrDefault(key, fallback) da vardır: emails.GetValueOrDefault(103, "no email").
Girdileri güncellemek ve silmek
Çıktı:
2
True
False
1
0
cart["milk"] += 1, milk henüz dictionary'de değilse KeyNotFoundException fırlatır, çünkü yazmadan önce okur. Remove olmayan bir anahtar için istisna fırlatmak yerine false döndürür, bu yüzden önce ContainsKey kontrol etmeye gerek yoktur.
Dolaşmak: KeyValuePair, Keys ve Values
Bir dictionary üzerindeki foreach, her biri bir Key ve bir Value içeren KeyValuePair<TKey, TValue> öğeleri üretir.
Çıktı:
Ana: 88
Ben: 72
Chloe: 95
Ana Ben Chloe
Total 255
77
pair.Value salt okunurdur, bu yüzden değerleri güncellemek indeksleyici üzerinden yazmak demektir. Son döngü anahtarların bir List<string> kopyası üzerinde döner; bu her zaman güvenlidir. Mevcut değerlerin üzerine yazarken doğrudan scores.Keys üzerinde dönmek .NET Core 3.0 ve sonrasında serbesttir ama .NET Framework'te InvalidOperationException fırlatır.
Aynı dictionary üzerindeki bir foreach içinde yeni bir anahtar eklemek her sürümde InvalidOperationException fırlatır. Dolaşma sırasında silmek .NET Framework'te istisna fırlatır, .NET Core 3.0'dan itibaren serbesttir. Her yerde çalışması gereken kod önce silinecek anahtarları toplar, sonra döngüden sonra siler.
C# 7 ve .NET Core 2.0 ya da sonrasıyla KeyValuePair döngü başlığında ayrıştırılabilir:
foreach (var (name, score) in scores)
{
Console.WriteLine($"{name}: {score}");
}
Bir dictionary ile saymak
Tekrarları saymak ders kitabı kullanımıdır. Geçerli sayıyı TryGetValue ile okuyun (olmayan bir anahtar 0 verir), sonra geri yazın.
Çıktı:
the 3
cat 1
and 2
dog 1
bird 1
Aynı şekil öğeleri gruplar: Dictionary<string, List<Order>>; listeyi TryGetValue ile alırsınız, yoksa bir tane oluşturup saklarsınız, sonra ona Add yaparsınız. Tek seferlik sayma ve gruplama için LINQ bunu tek bir ifadede yapar: words.GroupBy(w => w).ToDictionary(g => g.Key, g => g.Count()). Bkz. LINQ.
Bir karşılaştırıcıyla harf duyarsız anahtarlar
String anahtarlar varsayılan olarak birebir karşılaştırılır: "Apple" ve "apple" iki anahtardır. Bunu değiştirmek için constructor'a bir IEqualityComparer<string> verin.
Çıktı:
False
text/html
1
StringComparer.OrdinalIgnoreCase, HTTP başlıkları, dosya uzantıları ve kullanıcı adları gibi tanımlayıcılar için doğru seçimdir. Her anahtarı saklamadan önce .ToLower() çağırmak da çalışır, ama bir yerde unutmak kolaydır.
Kendi sınıfınızdan anahtarlar için dictionary anahtarın GetHashCode ve Equals metotlarını çağırır. Bunları override etmeyen bir sınıf referansla karşılaştırır, bu yüzden aynı alanlara sahip iki ayrı nesne farklı anahtarlardır. Bu çifti nasıl yazacağınız için HashSet sayfasına bakın.
Sıra, sıralama ve SortedDictionary
Bir Dictionary dolaşma sırası hakkında hiçbir söz vermez. Uygulamada yalnızca girdi eklenmiş bir dictionary ekleme sırasıyla dolaşılır, ama bir Remove'dan sonra sonraki bir Add boşalan yuvayı yeniden kullanıp daha önce görünebilir. Kod buna asla güvenmemelidir.
Bir sıraya ihtiyacınız olduğunda kullanım noktasında sıralayın ya da sıralı bir koleksiyon kullanın:
Çıktı:
Cairo 210
Lima 340
Oslo 520
By value, highest first:
Oslo 520
Lima 340
Cairo 210
Berlin, Cairo, Lima, Oslo
SortedDictionary<TKey, TValue> anahtarlarını her zaman sıralı tutar (dengeli bir ağaçtır), bu yüzden aramalar ve eklemeler O(1) yerine O(log n)'dir. Anahtar sırasıyla sık dolaşıyorsanız onu kullanın; sıraya yalnızca bir kez ihtiyacınız varsa normal bir dictionary'yi LINQ ile sıralayın. SortedList<TKey, TValue> daha az bellek kullanan ama büyüdüğünde eklemesi yavaş olan üçüncü bir seçenektir.
Hızlı başvuru
| Görev | Kod |
|---|---|
| Oluşturmak | new Dictionary<string, int>() |
| Eklemek ya da üzerine yazmak | d[k] = v |
| Eklemek, tekrarda istisna | d.Add(k, v) |
| Yalnızca yeniyse eklemek | d.TryAdd(k, v) |
| Okumak, yoksa istisna | d[k] |
| Güvenle okumak | d.TryGetValue(k, out var v) |
| Anahtar var mı | d.ContainsKey(k) |
| Silmek | d.Remove(k) (bool döndürür) |
| Boyut | d.Count |
| Anahtarlar, değerler | d.Keys, d.Values |
| Anahtara göre sıralı | d.OrderBy(p => p.Key) ya da SortedDictionary |
| Harf büyüklüğünü yok saymak | new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase) |
Yaygın hatalar
- Olmayan bir anahtarı
d[k]ile okumak.KeyNotFoundExceptionfırlatır;TryGetValuekullanın. - Var olabilecek bir anahtar için
Addçağırmak.ArgumentExceptionfırlatır; indeksleyiciyi ya daTryAdd'i kullanın. - Dictionary üzerindeki
foreachiçinde anahtar eklemek.InvalidOperationExceptionfırlatır; değişiklikleri toplayıp sonra uygulayın. - Dolaşma sırasına güvenmek. Sıralayın ya da
SortedDictionarykullanın. - Bir anahtar nesnesinin alanlarını ekledikten sonra değiştirmek. Hash kodu değişir ve girdi artık bulunamaz.
- Önce
ContainsKey, sonra indeksleyici. İki arama;TryGetValuetek arama yapar.
Sıkça Sorulan Sorular
C#'ta Dictionary.Add ile indeksleyici arasındaki fark nedir?
dict.Add(key, value) yeni bir girdi ekler ve anahtar zaten varsa ArgumentException fırlatır. dict[key] = value anahtar yeniyse girdiyi ekler, varsa değerin üzerine yazar ve bir tekrar için asla istisna fırlatmaz. TryAdd(key, value) yalnızca anahtar yeniyse ekler, aksi halde false döndürür.
C#'ta TryGetValue nasıl çalışır?
dict.TryGetValue(key, out var value) anahtar varsa true döndürür ve value'yu ayarlar; yoksa false döndürür ve value'yu tipinin varsayılanına ayarlar. Tek bir arama yapar (ContainsKey ardından dict[key] iki arama yapar) ve asla KeyNotFoundException fırlatmaz.
C#'ta bir Dictionary nasıl dolaşılır?
foreach (KeyValuePair<string, int> pair in dict) her girdiyi pair.Key ve pair.Value ile verir. Yalnızca anahtarlar ya da yalnızca değerler üzerinde dönmek için dict.Keys ya da dict.Values kullanın. O döngünün içinde dictionary'ye anahtar eklemeyin: InvalidOperationException fırlatır.
C# Dictionary sıralı mı?
Hiçbir sıra garanti edilmez. Yalnızca girdi eklenmiş bir dictionary genellikle ekleme sırasıyla dolaşılır, ama bir Remove'dan sonra yeni girdiler boşalan yuvayı doldurabilir ve sıra değişir. Bir sıraya ihtiyacınız olduğunda sıralayın: dict.OrderBy(p => p.Key) ya da her zaman anahtara göre dolaşan SortedDictionary<TKey, TValue> kullanın.
Dictionary anahtarları nasıl harf duyarsız yapılır?
Constructor'a bir karşılaştırıcı verin: new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase). Bundan sonra aramalar, Add ve ContainsKey için "Apple" ve "apple" aynı anahtardır. Karşılaştırıcı dictionary oluşturulduğunda sabitlenir.
C#'ta KeyValuePair nedir?
KeyValuePair<TKey, TValue>, bir dictionary'yi dolaştığınızda her girdi için size verdiği struct'tır. Salt okunur Key ve Value property'leri vardır, bu yüzden bir girdiyi onun üzerinden değiştiremezsiniz; bunun yerine dict[pair.Key] = newValue yazın (döngüden sonra ya da anahtarların bir kopyası üzerinde).