Массив в программировании: что это такое и чем он отличается от списка

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

Разбираем, как он устроен, какие бывают массивы и чем массив отличается от связного списка и от list в Python.

Примеры на C, Python и JavaScript и замер скорости на миллионе элементов.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 3100 статей автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3754 экспертных мнения Подписаться на Телеграм-канал
Обложка: Массив в программировании: что это такое и чем он отличается от списка

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

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

CheckroiCheckroiПодборка курсов по Python463 курса • 48 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Что такое массив простыми словами

Рой у ряда пронумерованных ячеек, как у массива

Возьмём камеру хранения на вокзале: ряд одинаковых ячеек, у каждой свой номер. Чтобы забрать сумку, вам не нужно открывать все дверцы по очереди, достаточно знать номер. Массив работает так же.

Три слова, без которых о массивах не поговорить:

  • элемент: одно значение внутри массива, например температура за один день;
  • индекс: номер элемента. Первый элемент имеет индекс 0, второй 1 и так далее;
  • длина: сколько элементов в массиве. Индекс последнего элемента на единицу меньше длины.

Вот так это выглядит в Python. Строго говоря, здесь список, но в Python он и есть основной массив языка, подробнее об этом ниже.

temps = [12, 15, 9, 21]
print(temps[0], temps[3])   # 12 21
print(temps[-1])            # 21: последний элемент
temps[2] = 10
print(temps)                # [12, 15, 10, 21]
print(len(temps))           # 4

Отрицательный индекс -1 работает в Python, но не в C и не в Java. Как ещё брать элементы с конца и целыми кусками, мы разбирали в статье про срезы в Python.

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

Классическое определение есть в справке Microsoft по C++: массив состоит из объектов одного типа, которые занимают непрерывную область памяти. Главное здесь слово «непрерывную». Проверим на коротком примере на C:

#include <stdio.h>

int main(void) {
    int prices[5] = {120, 340, 90, 560, 210};
    printf("sizeof(int) = %zu\n", sizeof(int));
    for (int i = 0; i < 5; i++) {
        printf("prices[%d] = %d, адрес %p\n", i, prices[i], (void *)&prices[i]);
    }
    return 0;
}

Вывод на нашем компьютере:

sizeof(int) = 4
prices[0] = 120, адрес 0x16dc56870
prices[1] = 340, адрес 0x16dc56874
prices[2] = 90, адрес 0x16dc56878
prices[3] = 560, адрес 0x16dc5687c
prices[4] = 210, адрес 0x16dc56880

Адреса у вас будут другими, но шаг останется тем же: каждый следующий элемент лежит ровно на 4 байта дальше, потому что одно число int занимает 4 байта. Поэтому компьютеру не нужно искать элемент. Он считает адрес по формуле:

адрес элемента = адрес начала массива + индекс × размер одного элемента

Отсюда и ответ на вопрос, почему индекс начинается с нуля. Индекс показывает, на сколько элементов нужно отступить от начала. Первый элемент лежит в самом начале, отступ ноль. Подробнее про объявление массивов, указатели и двумерные таблицы в C есть в отдельной статье о массивах в C.

Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Какие бывают массивы

Массивы различают по нескольким признакам сразу, поэтому один и тот же массив может быть, например, двумерным и динамическим.

Вид Что это значит Пример
Одномерный Простой ряд элементов, один индекс Оценки ученика за четверть
Многомерный Массив массивов, два индекса и больше Таблица, шахматная доска, пиксели картинки
Статический Длина задаётся при создании и дальше не меняется int a[10] в C, массивы в Java
Динамический Растёт и уменьшается во время работы программы list в Python, Array в JavaScript, ArrayList в Java
Ассоциативный Доступ по ключу вместо номера Словарь в Python, объект в JavaScript

Ассоциативный массив массивом в строгом смысле не является: элементы в нём не пронумерованы подряд. Название историческое, а в Python и JavaScript для этого есть словари и объекты.

Статические и динамические

В Java длина массива фиксируется навсегда. В учебнике Oracle сказано прямо: массив хранит фиксированное число значений одного типа, длина задаётся при создании, и после создания она не меняется.

Динамический массив устроен хитрее. В документации Python объясняется, что внутри list в CPython лежит массив переменной длины, связным списком он не является: когда место кончается, массив пересоздаётся, причём с запасом, чтобы следующие несколько добавлений обошлись без пересоздания. Это видно, если следить за размером пустого списка:

import sys
a = []
prev = sys.getsizeof(a)
print(len(a), prev)
for i in range(20):
    a.append(i)
    s = sys.getsizeof(a)
    if s != prev:
        print(len(a), s)
        prev = s
0 56
1 88
5 120
9 184
17 248

Размер растёт скачками: на первом, пятом, девятом и семнадцатом элементе. Между скачками Python кладёт новые элементы в уже выделенный запас. Цифры получены в Python 3.14 на 64-битном Mac, в других версиях шаги могут отличаться.

Однородные и разнородные

В C и Java все элементы массива одного типа. В JavaScript это не так: по справке MDN, ни размер массива, ни типы его элементов не фиксированы.

const mixed = [1, "два", true];
mixed[10] = 5;
console.log(mixed.length);   // 11
console.log(mixed[5]);       // undefined

Мы записали значение в ячейку 10, и длина сразу стала 11, а ячейки с 3 по 9 остались пустыми. Как правильно добавлять элементы и объекты в JS, разобрано в инструкции про push и spread.

Чем массив отличается от списка

Рой сравнивает ровный ряд ячеек и цепочку звеньев

Путаница возникает потому, что словом «список» называют две разные вещи.

  1. Связный список из учебников по алгоритмам. Элементы разбросаны по памяти, и каждый хранит ссылку на следующий (в двусвязном ещё и на предыдущий). Так устроен LinkedList в Java.
  2. Тип list в Python. Несмотря на название, внутри это динамический массив, как мы видели выше.

Когда спрашивают, чем массив отличается от списка, обычно имеют в виду первое: массив против связного списка. Вот главные различия:

Операция Массив Связный список
Взять элемент по индексу Сразу, по формуле адреса Нужно пройти по цепочке от начала или конца
Вставить в начало или середину Медленно: все элементы правее сдвигаются Быстро, если уже стоите на нужном месте
Добавить в конец Быстро, пока есть запас Быстро
Память Только сами элементы Элементы плюс ссылки на соседей

Так эти два класса описывает документация Java. Про LinkedList: операции с индексом проходят список с начала или с конца, смотря что ближе. Про ArrayList: получение элемента по индексу выполняется за постоянное время, добавление в конец за амортизированное постоянное, а остальные операции, грубо говоря, за линейное.

Проверим скорость на Python

Мы взяли список из миллиона чисел и тысячу раз повторили каждую операцию. Python 3.14.7, три прогона на Mac, разброс между прогонами в скобках.

1000 повторов операции Время
data[999_999]: взять элемент по индексу 0,015 мс (0,015–0,016)
append и pop: добавить и убрать в конце 0,1 мс (0,10–0,12)
insert(0) и pop(0): добавить и убрать в начале 0,38 с (0,38–0,40)
999_999 in data: найти значение перебором 5,4 с (5,33–5,39)

Доступ по индексу к последнему из миллиона элементов почти ничего не стоит. Вставка в начало в три с лишним тысячи раз медленнее вставки в конец, потому что Python каждый раз сдвигает миллион ссылок. Тот же эффект для pop(0) мы разбирали в статье про удаление из списка. А поиск значения перебором оказался самым дорогим: массив знает, где лежит элемент с номером, но не знает, под каким номером лежит нужное значение.

Вывод: массив хорош, когда вы много читаете по индексу и добавляете в конец. Частые вставки в начало лучше делать другой структурой, например collections.deque в Python, а частый поиск по значению отдать словарю или множеству.

Массив и список в Python

В Python есть и «настоящий» однородный массив, модуль array. По документации он компактно хранит числа и символы одного типа, а тип задаётся при создании. Сравним с обычным списком:

import sys
from array import array

nums = list(range(1_000_000))
packed = array("i", range(1_000_000))
print("list:", sys.getsizeof(nums), "байт")
print("array:", sys.getsizeof(packed), "байт")
try:
    packed.append(1.5)
except TypeError as e:
    print("TypeError:", e)
list: 8000056 байт
array: 4091948 байт
TypeError: 'float' object cannot be interpreted as an integer

Список занимает вдвое больше, и это ещё без самих чисел: list хранит только ссылки на объекты, а числа лежат в памяти отдельно. Массив array хранит сами значения по 4 байта и не пускает в себя дробь. На практике в Python почти всегда берут обычный список, а для больших числовых расчётов NumPy, пример есть в инструкции как суммировать список. Методы списка собраны в статье о списках в Python, а чем от них отличается неизменяемый кортеж, мы писали отдельно.

Основные операции с массивом

Что умеют делать с массивом почти во всех языках, и насколько это дорого:

  • чтение и запись по индексу: самая дешёвая операция, время не зависит от длины;
  • перебор: проход по всем элементам циклом. В Python индекс и значение сразу даёт функция enumerate();
  • добавление: в конец дёшево, в начало и середину дорого. Варианты для Python в инструкции как добавить элемент в список;
  • удаление: та же история, с конца быстро, из начала медленно;
  • поиск по значению: перебор всех элементов, если массив не отсортирован. По отсортированному массиву ищут бинарным поиском, а сортировать в Python удобно методом sort().

Многомерный массив читают двумя индексами подряд: сначала строка, потом столбец.

grid = [
    [1, 2, 3],
    [4, 5, 6],
]
print(grid[1][2])   # 6: вторая строка, третий столбец

Частые ошибки при работе с массивами

Пронумерованные кубики, разложенные ровным рядом

Ошибка на единицу

Самая частая ошибка новичков: цикл идёт до длины включительно. Элемента с индексом, равным длине, не существует.

temps = [12, 15, 9, 21]
for i in range(len(temps) + 1):
    print(temps[i])
IndexError: list index out of range

Решение: range(len(temps)), а лучше перебирать сами элементы, for t in temps, без индексов вовсе.

Выход за границы массива

Python и Java на чужой индекс выбрасывают ошибку, JavaScript молча возвращает undefined. А C не делает ничего:

int a[3] = {1, 2, 3};
int i = 3;
printf("%d\n", a[i]);

Clang 17 с флагом -Wall собрал этот код без единого предупреждения, а программа напечатала 1, которой в массиве нет. Это значение из памяти за концом массива, на другой машине оно будет другим. Так рождаются самые неприятные ошибки в C: программа не падает, а тихо считает неправильно.

Присваивание вместо копии

В Python, JavaScript и Java присваивание даёт массиву второе имя, копии при этом не появляется.

a = [1, 2, 3]
b = a
b.append(4)
print(a)          # [1, 2, 3, 4]
c = a.copy()
c.append(5)
print(a, c)       # [1, 2, 3, 4] [1, 2, 3, 4, 5]

Решение: нужна независимая копия, вызывайте copy() или срез a[:]. Для вложенных списков понадобится copy.deepcopy().

Удаление элементов прямо в цикле

Если удалять элементы из массива, по которому идёт цикл, элементы сдвигаются, и цикл перескакивает через следующий. Безопаснее собрать новый массив из нужных элементов. Почему так происходит, показано на примере в статье про удаление из списка, ссылка на неё есть выше.

Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 700 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

Где освоить программирование системно

С массива начинает любой программист, а за ним идут словари, множества, стеки, деревья и алгоритмы поиска и сортировки. Разобраться в них по одной статье трудно, проще на курсе с задачами и проверкой кода. Ниже курсы по Python, языку, на котором структуры данных удобнее всего изучать с нуля.

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
Профессия «Python-разработчик»
Перейти на сайт курса
SkillboxSkillbox157 335 ₽5987 ₽/мес.10 месяцевОбзор курса
Fullstack-разработчик на Python
Перейти на сайт курса
НетологияНетология137 000 ₽7125 ₽/мес.21 месяцОбзор курса
ДО Профессия Python-разработчик
Перейти на сайт курса
GeekBrainsGeekBrains149 001 ₽3167 ₽/мес.10 месяцевОбзор курса
Автоматизированное тестирование на Python
Перейти на сайт курса
SkillboxSkillbox122 226 ₽4108 ₽/мес.9 месяцевОбзор курса
Профессия Python-разработчик
Перейти на сайт курса
SkillFactorySkillFactory140 040 ₽3890 ₽/мес.10 месяцевОбзор курса

Больше программ — в полном каталоге курсов по Python

Часто задаваемые вопросы

Что такое массив простыми словами?

Это ряд пронумерованных ячеек под одним именем. В каждой ячейке лежит значение, а достают его по номеру, индексу. Нумерация в большинстве языков начинается с нуля.

Что такое индекс массива?

Индекс — номер элемента, то есть сколько элементов нужно отступить от начала массива. Поэтому первый элемент имеет индекс 0, а последний на единицу меньше длины массива.

Чем массив отличается от списка?

Если речь о связном списке, то массив хранит элементы подряд и отдаёт любой из них по индексу сразу, а связный список хранит ссылки на соседей и до нужного элемента идёт по цепочке. Зато вставлять в начало и середину связного списка дешевле. Тип list в Python, несмотря на название, устроен как динамический массив.

Чем динамический массив отличается от статического?

Длина статического массива задаётся при создании и дальше не меняется, так работают массивы в C и Java. Динамический массив растёт и уменьшается во время работы программы: list в Python, Array в JavaScript, ArrayList в Java.

Могут ли элементы массива быть разных типов?

В C и Java нет, все элементы одного типа. В Python и JavaScript в один список или массив можно положить число, строку и логическое значение вместе.

Есть ли массивы в Python?

Обычно роль массива играет список list. Для однородных чисел есть модуль array, он занимает меньше памяти, а для расчётов с большими таблицами чисел берут NumPy. Подробнее в статье о списках в Python.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!