Kalıp: değerleri boş olan bir map
Go'da set anahtar kelimesi ve standart kütüphanede set yoktur. Anahtarları elemanlar olan ve değerleri hiçbir şey taşımayan bir map bu işi görür.
struct{} boş struct tipidir ve struct{}{} onun tek değeridir. Sıfır bayt kaplar, bu yüzden map yalnızca anahtarları saklar.
Tüm map kuralları geçerlidir: anahtarlar karşılaştırılabilir olmalıdır, dolaşma sırası rastgeledir, nil bir map yazmada panic olur ve eşzamanlı yazmalar kilit gerektirir. Map'ler sayfası her birini anlatıyor.
map[T]struct{} mi, map[T]bool mu
Diğer yaygın yazım map[T]bool'dur. Eksik bir anahtar false döndürdüğü için daha iyi okunur:
seen := map[string]bool{}
seen["a"] = true
if seen["a"] { ... }
Ödünleşimler:
map[T]struct{} | map[T]bool | |
|---|---|---|
| Değer boyutu | 0 bayt | 1 bayt (artı hizalama) |
| Üyelik testi | _, ok := s[k] | s[k] |
| Belirsizlik | yok | s[k] = false üçüncü bir durumdur |
Birçok kod tabanının boş struct'ı tercih etmesinin asıl nedeni üçüncü satırdır: bool ile biri eninde sonunda s[k] = false yazar ve len(s) üye sayısı olmaktan çıkar. Küçük set'ler için bellek farkı önemli değildir.
Bir slice'tan tekrarları kaldırmak
Bir set'in en yaygın kullanımı tekrar ayıklamaktır. Bu, her değerin ilk görüldüğü yeri tutar ve sırayı korur:
Çıktı:
[b a c]
[3 1 2]
[1 2 3]
Birleşim, kesişim, fark
Küme cebiri birkaç döngüden ibarettir. Diğerinde üyelik test ederken küçük olan set'i dolaşın, çünkü her arama ortalamada sabit zamanlıdır.
sorted yalnızca çıktıyı kararlı yapmak için var. Bir set'i üzerinde range yaparak yazdırmak her çalıştırmada farklı bir sıra verir. maps.Keys ve slices.Sorted Go 1.23 gerektirir.
type set map[string]struct{} gibi adlandırılmış bir tip yine bir map'tir: ona aynı şekilde indeksler, üzerinde range yapar ve delete ile silersiniz; ayrıca ona metotlar ekleyebilirsiniz.
Küçük bir generic Set
Generics (Go 1.18) ile tek bir tip karşılaştırılabilir her eleman tipini karşılar. Çoğu program için bu yeterlidir:
Map'i bir struct içine sarmak struct{}{} gürültüsünü gizler ve map'in kurucu tarafından oluşturulmasını garanti eder; bu da nil map panic'ini ortadan kaldırır. Sorted bir metot olamaz: bir metot kendi tip parametrelerini tanımlayamaz ya da tipin comparable kısıtını daraltamaz, sıralama ise cmp.Ordered gerektirir. Kısıtları generics sayfası açıklıyor.
Tam özellikli bir set'e (thread-safe çeşitler, çok sayıda işlem) ihtiyacınız varsa github.com/deckarep/golang-set gibi üçüncü taraf paketler var. Çoğu kod için Go programcılarının kullandığı şey map kalıbı ya da bunun gibi 30 satırlık bir tiptir.
Struct set'leri
Karşılaştırılabilir alanlara sahip struct'lar dahil, karşılaştırılabilir her tip bir eleman olabilir. Bu, "bu çifti gördüm mü" kontrollerini doğrudan yapar:
type edge struct{ from, to string }
visited := map[edge]struct{}{}
visited[edge{"a", "b"}] = struct{}{}
Slice'lar ve map'ler set elemanı olamaz. Benzersiz slice'ları takip etmek için önce her birini karşılaştırılabilir bir anahtara dönüştürün; örneğin sabit boyutlu bir diziye ya da fmt.Sprint ile oluşturulmuş bir string'e.
Sık yapılan hatalar
- Başlatmayı unutmak.
var s map[string]struct{}nil'dir; ilk ekleme panic olur. - Bir set'i yazdırıp kararlı çıktı beklemek. Önce üyeleri sıralayın.
map[T]boolkullanıpfalsesaklamak. O zamanlenartık üyeleri saymaz. Kaldırmak içindeletekullanın.
Sıkça Sorulan Sorular
Go'da set tipi var mı?
Hayır. Standart kütüphanede set yoktur. Kalıp, değerleri hiçbir bilgi taşımayan bir map'tir: map[string]struct{}. Ekleme s[k] = struct{}{}, üyelik _, ok := s[k], silme delete(s, k) ve boyut len(s)'dir.
Go'da set için map[T]bool mu yoksa map[T]struct{} mi kullanmalıyım?
map[T]struct{} niyeti açık eder ve değerleri sıfır bayt tutar. map[T]bool daha doğal okunur (if seen[x]), çünkü eksik bir anahtar false döndürür. İkisi de doğrudur; bellek farkı yalnızca çok büyük set'lerde önemlidir. Birini seçin ve tutarlı olun.
Go'da bir slice'tan tekrarlar nasıl kaldırılır?
İlk görülen değeri sırayla korumak için dönün, görülen değerleri bir map[T]struct{} içinde takip edin ve yalnızca görülmemiş olanları ekleyin. Sıra önemli değilse sıralayıp komşu tekrarları atın: slices.Sort(s); s = slices.Compact(s).