Перейти к содержимому

Массив (array): понятие, устройства и практическое использование

Массив — один из самых простых и полезных инструментов в программировании. По сути это набор элементов одного типа, расположенных в памяти подряд. Представьте кирпичную стену: каждый кирпич держит своё место и имеет порядковый номер. Так же и элементы массива доступны по индексу.

Ключевые характеристики

  • Фиксированный порядок. Элементы хранятся последовательно и имеют индексы.
  • Нулевой индекс в большинстве языков. Большинство реализаций начинает отсчёт от нуля.
  • Быстрый доступ по индексу. Чтение или запись по индексу выполняется за постоянное время.
  • Статический или динамический размер. В C массивы статичны, в Python и JavaScript — динамические обёртки делают их растущими.

Как устроены в памяти

Элементы лежат подряд: адрес элемента i получается как адрес начала плюс i умноженное на размер одного элемента. Такая организация даёт преимущество кеша процессора — последовательный обход идёт очень быстро. Но у неё есть обратная сторона: вставка или удаление в середине требует сдвига элементов.

Основные операции и их сложности

  • Доступ по индексу: O(1)
  • Обход всех элементов: O(n)
  • Вставка/удаление в конце: O(1) для динамических массивов с резервом, иначе амортизированно O(1)
  • Вставка/удаление в середине: O(n)
  • Поиск по значению (линейный): O(n)

Типы массивов

  • Одномерные: список значений в одну строку.
  • Многомерные: матрицы, тензоры. Физически это обычно вложенные одномерные массивы или один большой массив с рассчитанными смещениями.
  • Динамические массивы: массивы, которые автоматически расширяются (например, ArrayList в Java, vector в C++).
  • Статические массивы: размер задаётся при создании и не меняется (обычно в C).

Типичные ошибки и подводные камни

  • Ошибка индексации. Off-by-one — самая частая причина падений.
  • Выход за границы массива. В языках без проверки границ это приводит к undefined behavior.
  • Неправильное управление памятью в статических реализациях — утечки или коррумпированные данные.
  • Частые вставки в середину при большом объёме данных. В таком случае стоит рассмотреть другие структуры.

Когда выбирать массив

Массив — это выбор, если важен быстрый произвольный доступ по индексу и вы знаете или можете контролировать объём данных. Для задач с частыми вставками и удалениями в середине лучше рассмотреть связный список или сбалансированное дерево. Для операций над большими числовыми матрицами массивы часто дают лучший результат за счёт кеш-локальности.

Короткие примеры кода

JavaScript — создание и перебор

const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

Python — список как динамический массив

arr = [1, 2, 3, 4]
arr.append(5)    # добавление в конец
for x in arr:
    print(x)

C — статический массив и доступ

int a[5] = {1, 2, 3, 4, 5};
int x = a[2];   /* доступ по индексу */

Java — ArrayList как динамический массив

import java.util.ArrayList;
ArrayList list = new ArrayList();
list.add(10);
int v = list.get(0);

Советы на практике

  • Если нужен частый доступ по индексу — используйте массив.
  • Если возможны частые вставки в середину — подумайте о других структурах.
  • При работе с большими объёмами данных учитывайте локальность памяти и выравнивание.
  • Тестируйте на краевых случаях: пустой массив, один элемент, максимальный размер.

Массив — базовый кирпич в архитектуре программ. Поняв его сильные и слабые стороны, можно стройно выстроить производительные решения.