Map va Set Object va Array'dan nimasi bilan farq qiladi?

MiddleJavaScript #javascript #arrays #data-structures

Savol

Map/Set bilan oddiy Object/Array o'rtasidagi farq nimada — kalit turlari, aylanib chiqish tartibi va tez-tez qo'shish, o'chirish, qidirishdagi unumdorlik bo'yicha?

Qisqa javob

Map kalit-qiymat juftliklarini saqlaydi, bu yerda kalit istalgan tur bo'lishi mumkin (obyektlar va funksiyalar dahil), kiritish tartibini kafolatlaydi va qo'shish/o'chirish/qidirish uchun O(1) beradi. Object kalit sifatida faqat satr va simvollarni ruxsat beradi (sonlar satrlarga keltiriladi). Set — istalgan turdagi noyob qiymatlar to'plami bo'lib, mavjudlikni O(1) da tekshiradi, O(n) da ishlaydigan Array.includesdan farqli o'laroq.

Batafsil javob

Object vs Map

  • Kalit turlari: Objectning kalitlari doim satrga (yoki Symbolga) keltiriladi — obj[1] va obj['1'] bu bitta kalit. Mapda kalit obyekt, funksiya, NaN, tur keltirishsiz istalgan narsa bo'lishi mumkin.
  • Tartib: zamonaviy dvigatellar obyektning satr kalitlari uchun kiritish tartibini saqlaydi, lekin butun sonli satr kalitlar ('1', '2') doim raqamli tartibda birinchi bo'lib keladi — bu aniq bo'lmagan va portativ bo'lmagan xatti-harakat. Map kalit turidan qat'i nazar, kiritish tartibini doim va kafolatli saqlaydi.
  • Hajm: Objectda kalitlar sonini bilishning o'rnatilgan usuli yo'q (Object.keys(obj).length), Mapda .size bor.
  • Iteratsiya: Object to'g'ridan-to'g'ri iteratsiyalanmaydi (for...of Object.entriessiz ishlamaydi), Map Iterable protokolini o'z-o'zidan amalga oshiradi.
  • Unumdorlik: Map kalitlarni tez-tez qo'shish/o'chirish uchun optimallashtirilgan — dvigatellar uning uchun Object prototipli zanjirning bir qismi bo'lishi bilan bog'liq yon effektlarsiz alohida xesh-strukturalar ishlatadi. Object xususiyatlarni tez-tez qo'shish/o'chirishda deoptimallashishi mumkin (V8'da "dictionary mode"ga o'tish).
  • Prototip: oddiy {} Object.prototypedan meros olingan xususiyatlarga ega, bu kalitlar to'qnashuviga olib kelishi mumkin ('toString', '__proto__'). Mapda bu muammo yo'q — bu toza saqlash joyi.

Array vs Set

  • Noyoblik: Set avtomatik ravishda dublikatlarni tashlab yuboradi, Array — yo'q, noyoblik uchun qo'lda mantiq yozish kerak.
  • Qidirish: Set.has() — amortizatsiyalangan O(1) (xesh-jadval), Array.includes()/indexOf() — O(n), chiziqli o'tish. Ko'p sonli mavjudlik tekshiruvlarida farq muhim ahamiyat kasb etadi.
  • O'chirish: Set.delete(value) — O(1), massivdan qiymat bo'yicha elementni o'chirish indexOf + splice, ya'ni O(n) talab qiladi.
  • Tartib: ikkalasi ham kiritish tartibini saqlaydi.
  • Indekslash: Arrayda sonli indekslar va ko'plab metodlar bor (map, filter, reduce), Setda bu yo'q — faqat add, delete, has, forEach, size.

Qachon nimani ishlatish: Object/Array — seriyalashtiriladigan ma'lumotlar (JSON), oddiy strukturalar uchun, bu yerda odatiy massiv metodlari kerak. Map/Set — kalitlar satr bo'lmaganda, kafolatli tartib muhim bo'lganda, mavjudlikni tez-tez tekshirish kerak bo'lganda yoki ko'p sonli qo'shish/o'chirishda unumdorlik muhim bo'lganda.

Misol

// Object faqat satr/simvol kalitlarni ruxsat beradi
const obj = {}
const keyObj = { id: 1 }
obj[keyObj] = 'value' // keyObj "[object Object]" satriga keltiriladi
obj['[object Object]'] = 'overwritten'
console.log(obj[keyObj]) // "overwritten" — to'qnashuv!

// Map istalgan turdagi kalitlarni keltirishsiz saqlaydi
const map = new Map()
const key1 = { id: 1 }
const key2 = { id: 2 }
map.set(key1, 'first')
map.set(key2, 'second')
console.log(map.get(key1)) // "first" — turli obyektlar to'qnashmaydi
console.log(map.size) // 2

// Object'dagi sonli kalitlar tartibi — kutilmagan
const weird = { b: 1, 2: 'two', a: 3, 1: 'one' }
console.log(Object.keys(weird)) // ['1', '2', 'b', 'a'] — sonlar oldinda

// Set: noyoblik va tez has()
const arr = [1, 2, 2, 3, 3, 3]
const unique = [...new Set(arr)]
console.log(unique) // [1, 2, 3]

const bigSet = new Set(Array.from({ length: 100000 }, (_, i) => i))
console.log(bigSet.has(99999)) // O(1) — bir zumda

const bigArr = Array.from({ length: 100000 }, (_, i) => i)
console.log(bigArr.includes(99999)) // O(n) — butun massiv bo'ylab o'tish

// Map obyekt/massivga va aksincha oson aylanadi
const entries = [['a', 1], ['b', 2]]
const fromEntries = new Map(entries)
console.log(Object.fromEntries(fromEntries)) // { a: 1, b: 2 }

Qo'shimcha savollar

  • WeakMap/WeakSet nima va ular xotirani boshqarish uchun nima bilan foydali?
  • Nega JSON.stringify Map va Setni to'g'ridan-to'g'ri seriyalashtira olmaydi?
  • V8 obyektni "dictionary mode"ga qanday o'tkazadi va bu unumdorlikka qanday ta'sir qiladi?