Array (Массив)
Array — упорядоченная коллекция элементов с числовым индексом, обеспечивающая O(1) доступ по индексу. Самая базовая и часто используемая структура данных.
Зачем нужно
Массив — фундамент программирования. Списки пользователей, товаров, сообщений, пикселей изображения — всё это массивы. Понимание внутреннего устройства помогает выбирать правильные операции и избегать O(n) там, где можно O(1).
Где используется
- Хранение любых коллекций данных
- Стек, очередь, буфер (реализуются на массиве)
- Матрицы (двумерные массивы)
- Буферы данных (TypedArray, ArrayBuffer)
- DOM-коллекции (NodeList → Array)
Предпосылки
- Как работает компьютер (память)
- Big O нотация
Массив в памяти (классический)
В языках как C, массив — непрерывный блок памяти:
Индекс: 0 1 2 3 4
Адрес: 100 104 108 112 116 (int = 4 байта)
Данные: [10] [20] [30] [40] [50]
Доступ к arr[3]: адрес = 100 + 3 × 4 = 112 → O(1)
JavaScript Array — особенности
JS-массивы — это объекты (не непрерывная память). V8 оптимизирует их в зависимости от содержимого:
// Dense array — оптимизирован как настоящий массив
const dense = [1, 2, 3, 4, 5]; // однотипные элементы, нет пропусков
// Sparse array — хранится как хэш-таблица (медленнее)
const sparse = ;
sparse[0] = 'a';
sparse[1000] = 'b'; // дырка в 999 элементов
// Смешанный — V8 может деоптимизировать
const mixed = [1, 'hello', true, { x: 1 }]; // разные типы
Создание массивов
// Литерал (рекомендуется)
const fruits = ['apple', 'banana', 'cherry'];
// Конструктор
const empty = new Array(5); // [<5 empty slots>]
const filled = Array(5).fill(0); // [0, 0, 0, 0, 0]
// Array.from — из итерируемого
const fromString = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
const range = Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
// Spread
const copy = [...fruits]; // поверхностная копия
// Array.of
const single = Array.of(5); // [5], а не [<5 empty>]
Операции и их сложность
Доступ и модификация
const arr = [10, 20, 30, 40, 50];
// Доступ по индексу — O(1)
arr[0]; // 10
arr[4]; // 50
arr.at(-1); // 50 (с конца)
// Длина — O(1)
arr.length; // 5
// Изменение по индексу — O(1)
arr[2] = 99; // [10, 20, 99, 40, 50]
Добавление/удаление
const arr = [1, 2, 3];
// В конец — O(1) амортизированно
arr.push(4); // [1, 2, 3, 4]
// С конца — O(1)
arr.pop(); // [1, 2, 3], вернул 4
// В начало — O(n)! Сдвигает все элементы
arr.unshift(0); // [0, 1, 2, 3]
// С начала — O(n)! Сдвигает все элементы
arr.shift(); // [1, 2, 3], вернул 0
// Произвольная позиция — O(n)
arr.splice(1, 0, 'a'); // [1, 'a', 2, 3] — вставка
arr.splice(1, 1); // [1, 2, 3] — удаление
Поиск
const arr = [10, 20, 30, 20, 40];
arr.indexOf(20); // 1 (первое вхождение) O(n)
arr.lastIndexOf(20); // 3 (последнее) O(n)
arr.includes(30); // true O(n)
arr.find(x => x > 25); // 30 O(n)
arr.findIndex(x => x > 25); // 2 O(n)
Трансформация (создают новый массив)
const nums = [1, 2, 3, 4, 5];
// map — преобразовать каждый элемент, O(n)
const doubled = nums.map(n => n * 2); // [2, 4, 6, 8, 10]
// filter — отфильтровать, O(n)
const evens = nums.filter(n => n % 2 === 0); // [2, 4]
// reduce — свернуть в одно значение, O(n)
const sum = nums.reduce((acc, n) => acc + n, 0); // 15
// slice — вырезать часть (не мутирует), O(k)
const middle = nums.slice(1, 4); // [2, 3, 4]
// concat — объединить, O(n+m)
const combined = nums.concat([6, 7]); // [1, 2, 3, 4, 5, 6, 7]
// flat — разворачивание, O(n)
[[1, 2], [3, [4]]].flat(Infinity); // [1, 2, 3, 4]
// flatMap — map + flat(1), O(n)
[1, 2, 3].flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]
Сортировка
const arr = [3, 1, 4, 1, 5, 9];
// sort мутирует массив! O(n log n)
arr.sort((a, b) => a - b); // [1, 1, 3, 4, 5, 9]
// Для строк — sort без аргумента (лексикографически)
['banana', 'apple', 'cherry'].sort(); // ['apple', 'banana', 'cherry']
// toSorted — НЕ мутирует (ES2023)
const sorted = arr.toSorted((a, b) => a - b);
// toReversed, toSpliced — НЕ мутирующие версии (ES2023)
const reversed = arr.toReversed;
const spliced = arr.toSpliced(1, 1, 'new');
Таблица сложности
| Операция | Сложность | Метод |
|---|---|---|
| Доступ по индексу | O(1) | arr[i] |
| Вставка в конец | O(1)* | push |
| Удаление с конца | O(1) | pop |
| Вставка в начало | O(n) | unshift |
| Удаление с начала | O(n) | shift |
| Поиск | O(n) | indexOf, find |
| Сортировка | O(n log n) | sort |
| slice | O(k) | slice(start, end) |
| splice | O(n) | splice |
* амортизированно
Паттерны работы с массивами
// Деструктуризация
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3, 4, 5]
// Swap элементов
[arr[0], arr[1]] = [arr[1], arr[0]];
// Удаление дубликатов
const unique = [...new Set([1, 2, 2, 3, 3])]; // [1, 2, 3]
// Группировка (Object.groupBy — ES2024)
const grouped = Object.groupBy(users, user => user.role);
// Chunk — разбиение на части
function chunk(arr, size) {
const chunks = ;
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
Частые ошибки
unshift/shiftв цикле. Каждый вызов — O(n), итого O(n^2). Используйте другую структуру- Мутирование при итерации. Удаление элементов через
spliceвforEach— непредсказуемое поведение sortбез компаратора для чисел.[10, 9, 1].sort()→[1, 10, 9](лексикографически!)- Путать поверхностную и глубокую копию.
[...arr]копирует ссылки, не вложенные объекты
// Подвох с sort
[10, 9, 1, 2, 21].sort(); // [1, 10, 2, 21, 9] — НЕПРАВИЛЬНО!
[10, 9, 1, 2, 21].sort((a, b) => a - b); // [1, 2, 9, 10, 21] — ПРАВИЛЬНО
Практика
- Реализуйте
flatten(arr, depth)— разворачивание на заданную глубину - Реализуйте
chunk(arr, size)безslice - Реализуйте
intersection(arr1, arr2)— общие элементы двух массивов - Сравните производительность
pushvsunshiftна 100 000 элементов
Связанные темы
- Stack (реализуется на массиве)
- Queue (реализуется на массиве)
- Big O нотация
- Linked List (альтернатива для частых вставок/удалений)
- Set (уникальные значения)
Ресурсы
- MDN — Array
- learn.javascript.ru — массивы
- V8 Blog — элементы и оптимизация массивов
- JavaScript Info — Array methods cheat sheet