Lists and Tuples
A deeper look at Elixir's two ordered collection types, how they're stored differently, and what that means for performance.
3 min read
Lists and tuples both hold ordered collections of values, and Elixir beginners often reach for either interchangeably at first. But they're stored very differently in memory, and that difference should drive which one you pick.
Lists are linked lists
An Elixir list is a linked list: each element points to the next. This has real consequences for performance.
list = [1, 2, 3, 4, 5]Prepending to the front is fast — constant time — because it just creates one new node pointing at the existing list:
[0 | list]
# => [0, 1, 2, 3, 4, 5]But getting the length, or accessing an arbitrary index, means walking the whole list node by node:
length(list) # O(n) -- has to traverse every element
Enum.at(list, 3) # O(n) -- walks to the 4th nodeIf you find yourself repeatedly indexing into a list or appending to its end in a loop, that's usually a sign a different data structure (a map, or building the list in reverse and prepending) would perform better.
Common list operations
list = [3, 1, 4, 1, 5]
Enum.sort(list)
# => [1, 1, 3, 4, 5]
Enum.uniq(list)
# => [3, 1, 4, 5]
list ++ [9, 2, 6]
# => [3, 1, 4, 1, 5, 9, 2, 6]
list -- [1, 4]
# => [3, 1, 5]++ concatenates and -- removes the first occurrence of each right-hand element from the left-hand list.
Tuples are fixed-size and contiguous
A tuple stores its elements contiguously in memory, similar to an array, which makes reading an element by index (elem/2) fast — constant time, no traversal:
point = {10, 20, 30}
elem(point, 1)
# => 20The tradeoff: modifying a tuple is expensive, because Elixir has to copy the entire tuple to produce a new one (there's no efficient "just change this slot"):
put_elem(point, 1, 99)
# => {10, 99, 30}That's fine for small, fixed-size tuples, but it's exactly why tuples aren't used for collections that grow or shrink — they're for a small, known number of related values, like {:ok, value}, {x, y} coordinates, or {status_code, body}.
Choosing between them
| | List | Tuple | |---|---|---| | Size | Variable | Fixed | | Best for | Sequences, iteration | A small group of related values | | Fast at | Prepending | Reading by index | | Slow at | Reading by index | Modifying an element |
A practical heuristic: if you're thinking "a collection of similar things I'll iterate over," reach for a list. If you're thinking "exactly these 2-4 specific values together," reach for a tuple. Function return values like {:ok, result} are tuples because the shape is fixed and known ahead of time — the caller pattern-matches on exactly that shape, which wouldn't work nearly as cleanly with a list.
Converting between them
Tuple.to_list({1, 2, 3})
# => [1, 2, 3]
List.to_tuple([1, 2, 3])
# => {1, 2, 3}These conversions exist, but needing them often is a hint that one of the two types was the wrong choice for the job in the first place — picking correctly up front, based on whether the size and shape are fixed or variable, avoids most of that friction.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.