Как использовать Map, List, Keyword и в чём их отличия?

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

List

List — это обычный связный список, упорядоченный, допускающий повторяющиеся элементы. Элементы добавляются в начало, а не в конец, что делает prepend-операции ([head | tail]) эффективными.

Создание списка:

list = \[1, 2, 3, 4\]

Доступ и работа:

\[head | tail\] = list
IO.inspect(head) # => 1
IO.inspect(tail) # => \[2, 3, 4\]

Методы:

  • Enum.map/2, Enum.reduce/3, Enum.filter/2 — для обхода и преобразования.

  • hd/1, tl/1 — для доступа к голове и хвосту.

  • [value | list] — быстрая вставка в начало.

  • ++, -- — объединение и вычитание списков.

Особенности:

  • Быстрые вставки в начало.

  • Медленный произвольный доступ (т.к. список связный).

  • Списки в Elixir не являются массивами.

Map

Map — это ассоциативный массив (хеш-таблица), хранящий пары ключ–значение. Ключи могут быть любого типа, доступ осуществляется за O(1).

Создание:

map = %{name: "Alice", age: 30}
  • Ключ :name — атом.

  • Значение может быть любым типом.

Доступ и обновление:

map\[:name\] # => "Alice"
map.name # => "Alice", если ключ — атом
updated = Map.put(map, :age, 31)

Методы:

  • Map.get/2, Map.put/3, Map.update/4, Map.delete/2

  • Map.merge/2 — объединение мапов.

  • Map.keys/1, Map.values/1

Особенности:

  • Неупорядоченная структура.

  • Ключи уникальны.

  • Быстрый произвольный доступ.

  • Поддерживает pattern matching по ключам:

%{name: n} = map

Keyword

Keyword — это список кортежей вида [{key, value}], где ключи — только атомы, а значения — любые типы. Он упорядочен, поддерживает дубликаты ключей.

Создание:

kw = \[name: "Alice", age: 30\]
\# То же самое, что: \[{:name, "Alice"}, {:age, 30}\]

Доступ:

Keyword.get(kw, :age) # => 30
kw\[:name\] # => "Alice"

Методы:

  • Keyword.get/2, Keyword.put/3, Keyword.delete/2

  • Keyword.merge/2, Keyword.has_key?/2

  • Keyword.keys/1, Keyword.values/1

Особенности:

  • Упорядоченный.

  • Можно иметь дубликаты ключей: [a: 1, a: 2].

  • Часто используется в опциях (options), передаваемых в функции:

MyModule.func(arg, timeout: 1000, retries: 3)

Сравнение по основным признакам

Признак List Map Keyword
Упорядоченность Да Нет Да
--- --- --- ---
Доступ по индексу Нет Да (по ключу) Да (по ключу-атомy)
--- --- --- ---
Тип ключей Нет Любые Только атомы
--- --- --- ---
Повтор ключей Нет Нет Да
--- --- --- ---
Вставка в начало Быстрая Не применимо Быстрая (как список)
--- --- --- ---
Производительность доступа O(n) O(1) O(n)
--- --- --- ---
Назначение Перечисление значений Ассоциативное хранилище Опции, аргументы функций
--- --- --- ---

Примеры использования

Списки:

Enum.map(\[1, 2, 3\], fn x -> x \* 2 end)

Мапы:

user = %{name: "Bob", admin: true}
Map.put(user, :admin, false)

Ключевые списки:

opts = \[timeout: 5000, retries: 3\]
Keyword.get(opts, :timeout)

Pattern matching:

%{a: x} = %{a: 10, b: 20}
\[x | \_\] = \[1, 2, 3\]
\[{k, v} | \_\] = \[name: "Elixir", lang: "BEAM"\]

Когда использовать:

  • List: когда нужен упорядоченный набор элементов, перебираемый последовательно.

  • Map: когда нужен быстрый доступ по ключу и строгая уникальность ключей.

  • Keyword: когда передаёшь опции в функцию или хочешь сохранить порядок и допускаешь дубли.

Таким образом, List, Map и Keyword в Elixir — это три фундаментальных инструмента для представления данных. Они не заменяют друг друга, а используются в зависимости от конкретной задачи, структуры и требований к производительности или семантике.