Sparround

Set və Map istifadə pattern-ləri

Set və Map eyni hash mexanizmi üzərində qurulub, amma fərqli suallara cavab verir:

  • Set — "bu dəyər varmı?" sualı. Yalnız üzvlüyü (membership) saxlayır, dəyər yoxdur. Əsas iki işi: təkrarları silmək (dedup) və "artıq gördüm" qeydi aparmaq.
  • Map — "bu açara nə uyğun gəlir?" sualı. Açar → dəyər indeksi qurur.

Sadə qayda: cavab bəli/xeyr-dirsə Set, cavab bir data-dırsa Map.

Ən tez-tez rast gəlinən pattern-lər:

  • Dedup: [...new Set(emails)]
  • Üzvlük yoxlaması: if (blocked.has(userId)) — massivdə includes O(n), Set-də O(1)
  • İndeks qurmaq: const byId = new Map(users.map(u => [u.id, u])) — sonra hər axtarış O(1)
  • Qruplaşdırma: açar → həmin açara aid elementlərin siyahısı
  • Sayğac: açar → neçə dəfə göründü

Açar bərabərliyi — burada dillər ciddi şəkildə ayrılır və müsahibələrdə ən çox tələ məhz buradadır.

JavaScript: Map/Set SameValueZero müqayisəsi işlədir. Primitiv dəyərlər üçün bu, === kimidir (NaN istisna olmaqla — NaN özünə bərabər sayılır). Obyekt açarları isə referansa görə müqayisə olunur — m.set({ id: 1 }, 'a') yazıb sonra m.get({ id: 1 }) çağırsan, cavab undefined olur, çünki bu, ikinci, tamam başqa obyektdir.

JS-də dəyərə görə açar lazımdırsa, açarı özün stringə çevirməlisən (` ${a}:${b} `).

Kotlin: bərabərlik equals()/hashCode() cütü ilə müəyyən olunur. data class bu ikisini avtomatik generasiya etdiyi üçün dəyərə görə açar pulsuz alınır: Point(1, 2) == Point(1, 2)true, deməli map-da eyni açardır. Adi class isə referansa görə müqayisə olunur — JS ilə eyni tələ.

Dart: default davranış referansa görədir. Dəyərə görə istəyirsənsə, operator ==hashCode-u birlikdə override etməlisən — birini yazıb o birini unutmaq map-ı sındırır. Boilerplate-dən qaçmaq üçün package:equatable və ya freezed işlədilir; Dart 3-də record-lar ((1, 2)) artıq dəyərə görə bərabərdir və hazır açar kimi istifadə oluna bilər.

SualJavaScriptKotlinDart
Dəyərə görə açar necə alınır?Əl ilə string açar qurmaqla`data class` — avtomatik`==` + `hashCode` override, və ya Dart 3 record
İterasiya sırası`Map`/`Set` əlavə sırasını saxlayır (zəmanətli)`HashMap` — zəmanət yox; `LinkedHashMap` — əlavə sırası; `TreeMap` — sıralı`{}` literalı `LinkedHashMap`-dir — əlavə sırası; `HashMap` — zəmanət yox
Sıralı açar sırası lazımdırsaYerləşik yoxdur — açarları çıxarıb `sort` et`sortedMapOf` / `java.util.TreeMap``SplayTreeMap` (`dart:collection`)
Ölçü`map.size``map.size``map.length`

Adi obyekt/record nə vaxt Map deyil?

JS-də çoxları {} obyektini map kimi işlədir. Kiçik, sabit konfiqurasiya üçün bu normaldır, amma dinamik açarlar üçün Map demək olar həmişə düzgün seçimdir:

  • Obyektin açarları yalnız stringsymbol ola bilər — obj[1] əslində obj["1"]-dir. Map isə istənilən tipi, hətta obyekti açar kimi qəbul edir.
  • Obyekt prototip zəncirini miras alır: "toString" in obj heç nə əlavə etməsən də true-dur. Map təmiz başlayır.
  • constructor, __proto__ kimi açarlar gözlənilməz nəticələr verir (prototype pollution riski).
  • Ölçü: Object.keys(obj).length O(n), map.size O(1).
  • Tez-tez əlavə/silmə: Map bunun üçün optimallaşdırılıb, obyekt isə deyil.

Qayda: açarlar əvvəlcədən məlum və sabitdirsə obyekt/record, açarlar iş vaxtı gəlirsə Map. Kotlin və Dart-da bu qarışıqlıq yoxdur, çünki orada obyekt (data class) və map dil səviyyəsində fərqli şeylərdir.

İnterview ipucu. Bu mövzuda ən çox verilən sual: "Obyekti Map-da açar kimi istifadə edə bilərsən?" Zəif cavab sadəcə "bəli"-dir. Güclü cavab dili ayırır:

"Bəli, amma bərabərliyin necə müəyyən olunduğu vacibdir. JS-də obyekt açarları referansa görə müqayisə olunur, ona görə eyni məzmunlu iki ayrı obyekt fərqli açarlardır — dəyərə görə açar lazımdırsa özüm string açar qururam. Kotlin-də data class equals/hashCode generasiya etdiyi üçün dəyərə görə işləyir. Dart-da ==hashCode-u birlikdə override etməliyəm."

İkinci klassik sual: "Object.keys().length ilə map.size arasında fərq?" — cavab O(n) və O(1). Müsahiblər bu kiçik detalı namizədin performans hissiyyatını yoxlamaq üçün işlədirlər.

Ən çox buraxılan səhv: iterasiya sırası haqqında yanlış zəmanət vermək. JS Map sırası zəmanətlidir, Kotlin HashMap sırası isə YOX. Bu ikisini qarışdırmaq real bug mənbəyidir.

📚 Mənbələr və sənədlər

  • Setrəsmideveloper.mozilla.org