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əincludesO(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 == və 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.
| Sual | JavaScript | Kotlin | Dart |
|---|---|---|---|
| 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ırsa | Yerləş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
stringvəsymbolola bilər —obj[1]əslindəobj["1"]-dir.Mapisə istənilən tipi, hətta obyekti açar kimi qəbul edir. - Obyekt prototip zəncirini miras alır:
"toString" in objheç nə əlavə etməsən dətrue-dur.Maptə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).lengthO(n),map.sizeO(1). - Tez-tez əlavə/silmə:
Mapbunun üçü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 == və 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