Sparround

Dillərin daxili kolleksiyaları

Data strukturlarını əl ilə yazmağı öyrənmək müsahibə üçün lazımdır, amma real kodda demək olar həmişə dilin hazır kolleksiyasını seçirsən. Vacib olan hər birinin daxildə nə olduğunu və əməliyyatların qiymətini bilməkdir — çünki səhv kolleksiya seçimi ən çox rast gəlinən gizli performans problemidir.

Üç sual kifayətdir:

  • Nə üzərində qurulub? Dinamik massiv, hash table, linked list, yoxsa balanslı ağac?
  • Sıra saxlanılırmı? Daxiletmə sırası, sıralanmış sıra, yoxsa heç bir zəmanət yoxdur?
  • Hansı əməliyyat əsasdır? İndekslə çıxış, açara görə axtarış, uclarda əlavə/silmə, yoxsa üzvlük yoxlaması?

Bu üç sualın cavabı kolleksiya seçimini demək olar avtomatik edir.

StrukturJavaScriptKotlinDartDaxildə nə var
Dinamik massiv`Array``List` / `MutableList` (`ArrayList`)`List`Ardıcıl buffer + length; indeks O(1), sona əlavə amortized O(1)
Hash map`Map` (daxiletmə sırasını saxlayır) / sadə `object``HashMap`, `LinkedHashMap` (default `mapOf`)`Map` (default `LinkedHashMap`)Hash table; get/put/delete orta halda O(1)
Hash set`Set``HashSet`, `LinkedHashSet` (default `setOf`)`Set` (default `LinkedHashSet`)Yalnız açarlardan ibarət hash table; `has` O(1)
Deque / QueueYoxdur — `Array` və ya əl ilə`ArrayDeque``Queue`, `ListQueue`, `DoubleLinkedQueue`Circular buffer (və ya linked list); hər iki ucda O(1)
Linked listYoxdur`java.util.LinkedList` (tövsiyə olunmur)`LinkedList` (`dart:collection`)Node + pointer-lər
Sıralanmış map/setYoxdur`sortedMapOf`, `TreeMap` (JVM)`SplayTreeMap`, `SplayTreeSet`Balanslı ağac; əməliyyatlar O(log n), sıra saxlanılır
Zəif istinadlı map`WeakMap`, `WeakSet``WeakHashMap` (JVM)`Expando`, `WeakReference`Açar GC olunanda yazı da silinir — keş üçün

Dil üzrə praktik qeydlər — müsahibədə bunlar konkretlik siqnalıdır.

JavaScript: Map sadə object-dən üstündür — açar istənilən tip ola bilər (obyekt daxil), daxiletmə sırası zəmanətlidir, size O(1)-dir və prototip çirklənməsi riski yoxdur. Sadə object-də açarlar həmişə string/symbol-a çevrilir. Set üzvlük yoxlamasını includes-un O(n)-indən O(1)-ə endirir. Növbə üçün hazır tip yoxdurshift() O(n) olduğu üçün ya head indeksi saxlamalı, ya öz strukturunu yazmalısan. Massiv metodları arasında push/pop O(1), shift/unshift/splice isə O(n)-dir.

Kotlin: immutable və mutable interfeyslər ayrıdır — listOf vs mutableListOf, mapOf vs mutableMapOf. Bu, niyyəti tip səviyyəsində ifadə edir. mapOf/setOf daxiletmə sırasını saxlayan LinkedHashMap/LinkedHashSet qaytarır; sıra əhəmiyyətsizdirsə HashMap bir qədər sürətlidir. Stack və queue üçün doğru seçim ArrayDeque-dir — köhnə java.util.Stack (sinxronlaşdırılmış, Vector-dan miras) və LinkedList istifadə edilməməlidir. Data class-lar equals/hashCode-u avtomatik yaradır, ona görə map açarı kimi təhlükəsizdir.

Dart: List, Map, Set üçün literal sintaksis var ([], {}, {1, 2}) — diqqət: boş {} Map-dir, boş Set üçün <int>{} yazmaq lazımdır. Queue dart:collection-dadır və ListQueue (circular buffer) default implementasiyadır. const kolleksiyalar kompilyasiya vaxtında yaradılır. Kolleksiya metodlarının əksəriyyəti lazy `Iterable` qaytarır (map, where) — toList() çağırana qədər iş görülmür; bu, həm üstünlük, həm də təkrar hesablama mənbəyidir.

Hansını seçməli — praktik qayda:

  • Sıra vacibdir, indekslə çıxış lazımdır → List/Array
  • Açara görə axtarış → Map (orta halda O(1))
  • Unikallıq və ya üzvlük yoxlaması → Set
  • Hər iki ucda əlavə/silmə, növbə və ya stack → Deque (ArrayDeque, Queue)
  • Həmişə sıralanmış qalmalıdır → sorted map/set (O(log n)) və ya bir dəfə sort()
  • Prioritetə görə çıxarma → priority queue / heap (O(log n))

Müsahibədə isə fərqli qayda işləyir. Səndən adətən əl ilə yazmağı gözləyirlər: linked list, stack, queue, hash map-in sadə variantı, binary search tree, heap. Səbəb odur ki, bu strukturlar pointer manipulyasiyası, sərhəd halları və mürəkkəblik təhlilini yoxlamaq üçün ideal vasitədir.

Ən yaxşı davranış — hər ikisini birləşdirmək: "Real layihədə `ArrayDeque` istifadə edərdim, çünki o, circular buffer üzərində qurulub və hər iki ucda `O(1)` verir. Amma burada əl ilə yazmağımı istəyirsinizsə, belə edərəm..." Bu cümlə həm praktik yetkinlik, həm də nəzəri hazırlıq göstərir.

İnterview məsləhəti. Çox verilən sual: "`Map` ilə sadə `object` (və ya `HashMap` ilə `LinkedHashMap`) arasında nə fərq var?" Cavabda üç oxa toxun: açar tipləri, sıra zəmanəti, performans və API. Konkret detal ver — məsələn, JS-də object açarları string-ə çevirir, ona görə obj[1]obj['1'] eyni xanadır; Map-də isə fərqlidir.

İkinci klassik sual: "Hash map orta halda `O(1)`-dirsə, ən pis halda nədir?"O(n), bütün açarlar eyni bucket-ə düşəndə (pis hash funksiyası və ya qəsdən hazırlanmış giriş — hash flooding hücumu). Müasir implementasiyalar bucket-i ağaca çevirərək bunu O(log n)-ə endirir. Bu detalı bilmək yaxşı təəssürat buraxır.

Mutable obyekti map açarı kimi işlətmək — ən çox rast gəlinən real buq: açarın hash-i əlavədən sonra dəyişirsə, elementi bir daha tapmaq mümkün olmur. Ona görə açarlar immutable olmalıdır (Kotlin data class ilə val sahələr, Dart-da immutable dəyərlər).

Digər tipik səhvlər: dövr içində list.contains() çağırıb O(n²) yaratmaq (əvəzinə Set); HashMap-dən sıra gözləmək; Kotlin-də equals/hashCode təyin etməmiş sinfi map açarı kimi işlətmək; JS-də obyekt açarını JSON.stringify ilə düzəltməyə çalışmaq (Map onsuz da obyekt açarlarını dəstəkləyir).

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