Великий O масивів JavaScript


105

Масиви в JavaScript дуже легко змінювати, додаючи та видаляючи елементи. Це дещо маскує той факт, що більшість мовних масивів мають фіксований розмір і потребують складних операцій для зміни розміру. Здається, що JavaScript дозволяє легко писати неякісний код масиву. Це призводить до питання:

Яку ефективність (з точки зору великої складності у часі) можна очікувати від реалізацій JavaScript щодо продуктивності масиву?

Я припускаю, що всі розумні реалізації JavaScript мають максимум такі великі O.

  • Доступ - O (1)
  • Додавання - O (n)
  • Попередньо - O (n)
  • Вставка - O (n)
  • Видалення - O (n)
  • Заміна - O (1)

JavaScript дозволяє попередньо заповнити масив до певного розміру, використовуючи new Array(length)синтаксис. (Питання про бонус: чи створюється масив таким чином O (1) або O (n)) Це більше схоже на звичайний масив, і якщо він використовується як масив попереднього розміру, може дозволити додавання O (1). Якщо додана кругова буфера логіки, ви можете досягти O (1) попередньо. Якщо використовується масив, що динамічно розширюється, O (log n) буде середнім випадком для обох цих.

Чи можу я очікувати кращої ефективності для деяких речей, ніж мої припущення? Я не сподіваюся, що щось описується в будь-яких специфікаціях, але на практиці може бути так, що всі основні реалізації використовують оптимізовані масиви за кадром. Чи є в роботі динамічно розширюються масиви чи якісь інші алгоритми підвищення продуктивності?

PS

Причиною, що мені цікаво, є те, що я досліджую деякі алгоритми сортування, більшість з яких, здається, припускають додавання та видалення операцій O (1) при описі загального великого О.


6
Конструктор Array з розміром майже марний у сучасних реалізаціях JavaScript. Він майже нічого не робить у цій формі одного параметра. (Встановлюється, .lengthале це стосується цього.) Масиви дійсно не сильно відрізняються від простих екземплярів Object.
Pointy

3
Встановлення lengthвластивості та попередньо розподілити простір - це дві абсолютно різні речі.
Pointy

1
@Pointy: Чи очікую я занадто багато, коли очікую, що налаштування array[5]на a new Array(10)- це O (1)?
Кендалл Фрей

1
Хоча ECMAScript не визначає, як реалізується об’єкт Array (він визначає лише деякі смислові правила), дуже можливо, що різні реалізації будуть оптимізувати для очікуваних випадків (наприклад, мати "реальний масив" резервного копіювання для масивів менше ніж n n розмірів ). Я не такий кмітливий у реалізації, але був би дуже здивований, якби це десь не було зроблено ...

5
@KendallFrey "Найкраща відповідь", ймовірно, напише кілька тестів jsperf для різних шаблонів n / доступу та побачить, що з цього виходить ;-)

Відповіді:


111

ПРИМІТКА. Хоча ця відповідь була правильною у 2012 році, двигуни сьогодні використовують дуже різні внутрішні уявлення як для об’єктів, так і для масивів. Ця відповідь може бути, а може бути істинною.

На відміну від більшості мов, які реалізують масиви з, ну, масивами, у Java-масивах - це об'єкти, а значення зберігаються в хеш-таблицю, як і звичайні значення об'єкта. Як такий:

  • Доступ - O (1)
  • Додавання - Амортизований O (1) (іноді потрібно змінити розмір хештеля; зазвичай потрібно лише вставлення)
  • Попередньо - O (n) через unshift, оскільки воно вимагає перепризначення всіх індексів
  • Вставка - амортизований O (1), якщо значення не існує. O (n), якщо ви хочете змінити існуючі значення (наприклад, використовуючи splice).
  • Видалення - Амортизований O (1) для видалення значення, O (n), якщо ви хочете перепризначити індекси через splice.
  • Заміна - O (1)

Взагалі, встановлення або вимкнення будь-якого ключа в диктаті амортизується O (1), і те саме стосується масивів, незалежно від того, що таке індекс. Будь-яка операція, яка вимагає перенумерування існуючих значень, є O (n) просто тому, що вам потрібно оновити всі впливові значення.


4
Чи не повинно бути вперед O (n)? Оскільки всі індекси потрібно змістити. Те ж саме для вставки та видалення (при довільному індексі та зміщенні / згортанні елементів).
nhahtdh

2
Крім того, lengthвстановлюється мутація масиву, чи getвін збирається отримати довжину і, можливо, запам'ятати її?
alex

27
Варто згадати цю відповідь більше не вірно. Сучасні двигуни не зберігають масиви (або об'єкти з індексованими цілими клавішами) як хешбелі (але, як добре ... масиви, як у C), якщо вони рідкі. Щоб почати тут, це "класичний" орієнтир, що ілюструє це
Бенджамін Груенбаум

4
Це визначено стандартом чи це просто загальна реалізація в двигунах JS? Що про V8?
Альберт

4
@BenjaminGruenbaum було б добре, якби ви могли трохи розробити, як вони зберігаються. Або дайте деякі джерела.
Сід

1

гарантія

Немає визначеної гарантії складності часу для жодної операції масиву. Дія масивів залежить від основної структури даних, яку обирає двигун. Двигуни також можуть мати різні представлення і перемикатися між ними залежно від певної евристики. Початковий розмір масиву може бути або не бути таким евристичним.

реальність

Наприклад, V8 використовує (станом на сьогодні) і хеш - таблиці, і списки масивів для представлення масивів. Він також має різні різні зображення для об'єктів, тому масиви та об'єкти не можна порівнювати. Тому доступ до масиву завжди кращий, ніж O (n), і навіть може бути таким же швидким, як доступ до масиву C ++. Додавання дорівнює O (1), якщо ви не досягнете розміру структури даних, і її потрібно масштабувати (що дорівнює O (n)). Попередження гірше. Видалення може бути ще гірше, якщо ви робите щось на зразок delete array[index](не!), Оскільки це може змусити двигун змінити своє представлення.

порада

Використовуйте масиви для числових структур даних. Ось для чого вони призначені. Ось для чого двигуни оптимізують їх. Уникайте рідких масивів (або, якщо вам доведеться, очікуйте гіршої продуктивності). Уникайте масивів із змішаними типами даних (оскільки це робить внутрішні уявлення складнішими ).

Якщо ви дійсно хочете оптимізувати певний двигун (і версію), перевірте, чи є вихідний код на абсолютну відповідь.


Зачекайте секунду, у нас можуть бути масиви зі змішаними типами даних? Javascript такий класний!
Анураг

@Anurag точно, але в 99% випадків ця функція вам не знадобиться
Desiigner
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.