list vs tuple vs deque vs array — which and why?

Pick by the operation you do most. list for ordered mutable data with random access; tuple when it must not change or must be a dict key; deque when you touch the front; array when you have millions of numbers; set when you only ask "is it in there?".

Overview

The three, in one table

 listtupledeque
MutableYesNoYes
HashableNoYes, if contents areNo
Append rightO(1) amortised—O(1)
Append leftO(n)—O(1)
Pop leftO(n)—O(1)
Index x[i]O(1)O(1)O(n)
SlicingYesYesNo
Memory per elementHigherLowerSimilar to list
Import neededNoNocollections

Two rows decide almost every real choice: deque is O(1) at both ends but O(n) to index, and tuple is hashable so it can be a dict key or a set member.

Lists & arraysConceptualEasy

Step through it

What to watch

  • The front of a list is the trap — O(n), where a deque is O(1).
  • Hashability is the real tuple/list distinction, not immutability for its own sake.
  • An array stores values; a list stores references to them.

Say this out loud

"Tuple if it's fixed or needs to be hashable - it can be a dict key, a list can't. deque if I'm touching the front, because list.pop(0) is O(n). array or numpy for large numeric data, since a list stores pointers rather than values."

list vs tuple vs deque vs array — which and why?

When would you use a tuple instead of a list? What about deque or array?

Run it

The five containers measured rather than described: the operation each is fast at, the memory each uses for the same thousand integers, and the two errors that pick the container for you.

1Python
Output

When to use each

list is the default. Random access, sorting, slicing, appending at the end — all good. Only reject it when you need something it cannot do.

tuple when the sequence is a fixed record rather than a collection, or when it must be hashable:

2Python
Output

The hashability is the hard requirement — a list simply cannot be a dict key. The immutability is also a signal to the reader that the contents are not going to change, which is why returning a tuple from a function that produces a fixed set of values is idiomatic.

deque when you add or remove at the front:

3Python
Output

maxlen is the underrated feature. A bounded deque discards from the opposite end automatically, which gives you a rolling window or a fixed-size log buffer with no bookkeeping at all.

The performance trap that matters

list.pop(0) and list.insert(0, x) are O(n), because every remaining element shifts one position. Inside a loop, that makes the whole operation quadratic:

4Python
Output

Roughly five billion element moves. Swapping in a deque makes it linear:

5Python
Output

Any breadth-first search, task queue, or producer-consumer loop should use a deque. This is the single most common reason a straightforward-looking Python loop is slow, and it is invisible in small tests — a thousand elements finishes fine, and a hundred thousand does not.

The mirror-image mistake also exists: reaching for a deque and then indexing it in a loop. dq[i] walks the internal block list, so a positional loop over a deque is O(n²). Use a list when you index, a deque when you push and pop at the ends.

tuple versus list

The textbook answer is "tuples are immutable", which is true and not the point. The consequence is that a tuple is hashable (if its contents are), so it can be a dictionary key or a set member — which is why coordinates, database rows and cache keys are tuples.

The secondary signal is meaning. A list is a homogeneous sequence of unknown length; a tuple is a fixed-size record where position carries meaning. (x, y) is a point; [x, y] is two numbers.

deque versus list

collections.deque is a doubly linked list of blocks, so appendleft and popleft are O(1) where the list equivalents are O(n). Any queue, BFS frontier or "last N items" buffer should be a deque.

The trade is random access: d[5000] walks the blocks, so it is O(n). If you index into the middle, keep the list. A deque also takes maxlen, which turns it into a fixed-size ring buffer that discards the oldest entry automatically.

array and the memory question

A list holds references, so a list of a million integers is a million pointers plus a million integer objects. array.array packs the values inline in one typed block, which is several times smaller and much friendlier to the cache.

For real numeric work the answer is numpy, which adds vectorised operations on top of the same packed layout. Mentioning that a list of numbers is a list of pointers is usually the specific thing an interviewer is listening for.

Why tuples are smaller and faster

6Python
Output

A list over-allocates so that append is amortised O(1), and it stores a capacity alongside its length. A tuple is fixed at creation, so it needs neither — less memory, one less indirection, and marginally faster iteration.

CPython also caches small tuples for reuse, and constant tuples are built at compile time:

7Python
Output

So for x in (1, 2, 3) is very slightly faster than the list version. This is a real but tiny effect, and not a reason to choose a tuple — choose based on mutability and hashability, and take the memory win as a side benefit.

Immutability is shallow

A tuple's immutability applies to its own slots, not to what they point at:

8Python
Output

The tuple guarantees its references will not change, and says nothing about the objects behind them. That is also why a tuple containing a list is unhashable: hashing recurses into the contents, so one mutable element makes the whole thing unhashable.

The practical rule: a tuple is only usable as a dict key if everything inside it is hashable too. Convert nested lists to tuples, and sets to frozenset.

The rest of the family

NeedUse
Fixed record with named fieldsNamedTuple or dataclass(frozen=True)
Queue between threadsqueue.Queue — deque plus locking
Priority queueheapq on a list
Array of numbers, compactarray.array or NumPy
Sorted insertionbisect.insort on a list
Immutable setfrozenset
Ring bufferdeque(maxlen=n)

NamedTuple is the natural upgrade once a tuple's positions acquire meaning. point.x beats point[0], it stays a tuple, and it costs no extra memory:

9Python
Output

deque is thread-safe for append and popleft individually, because those are atomic at the C level. That does not make a check-then-pop sequence safe — use queue.Queue when several threads coordinate through it.

Questions people ask

Why is list.pop(0) slow? Every subsequent element shifts down one slot. Use deque.popleft.

Can I index a deque? Yes, and at O(n). Indexing in a loop is quadratic.

Why can a tuple not be a dict key sometimes? If it contains a mutable object, it is unhashable.

Is a tuple actually faster? Marginally, from less allocation and compile-time constants. Not a deciding factor.

What about slicing a deque? Unsupported. Use itertools.islice, or a list if slicing is central.

deque or queue.Queue for threads? Queue when threads coordinate; deque when a single owner mutates it.

Does tuple copying help? tuple(lst) snapshots the references — a shallow copy, so shared mutable elements are still shared.

Recap in one screen

  • list is the default; tuple when the sequence is a fixed record or must be hashable; deque when you touch both ends.
  • list.pop(0) and insert(0, x) are O(n) and turn ordinary loops quadratic — use deque for queues and BFS.
  • Indexing a deque is O(n), so do not loop over it positionally.
  • Tuple immutability is shallow: the slots are fixed, the objects inside may not be, and one mutable element makes it unhashable.
  • deque(maxlen=n) gives a rolling window for free; NamedTuple gives a tuple with named fields.

How the code works

The five containers measured rather than described: the operation each is fast at, the memory each uses for the same thousand integers, and the two errors that pick the container for you.

How the code works

  1. lst.insert(0, 1) versus dq.appendleft(1)The same logical operation, O(n) against O(1). Doing it in a loop is the most common accidental quadratic in Python, and it usually appears as a hand-rolled queue.
  2. dq[N // 2]Where the deque loses. It is a linked list of blocks, so indexing into the middle walks them. If you index, keep the list.
  3. sys.getsizeof(numbers) versus the arrayA list stores references; an array stores the values. That is why the array is smaller and why it can hold only one type — and it is the specific point interviewers listen for.
  4. deque(maxlen=3)A fixed-size ring buffer for free: appending past the limit drops the oldest. That is the "last N events" structure, without any bookkeeping.

Change one thing

  • Compare sys.getsizeof for a list and a tuple of the same items. The tuple is smaller because it never has to leave growth room.
  • Build a numpy array of a million floats and compare with a list. The gap is much larger than the array module's.

Where this runs

Real CPython, compiled to WebAssembly and running on your own machine — nothing is uploaded. The first run takes a few seconds while the interpreter downloads; after that it is immediate. Need more room, or want to paste your own attempt? Use the Python compiler.

Check yourself

0 of 3

Answer without scrolling back up.

  1. The most practical difference between a tuple and a list is that a tuple:

  2. You need a queue. Which container?

  3. array.array uses less memory than a list of the same integers because it:

Cheat sheet

list vs tuple vs deque vs array — which and why?

Pick by the operation you do most. list for ordered mutable data with random access; tuple when it must not change or must be a dict key; deque when you touch the front; array when you have millions of numbers; set when you only ask "is it in there?".

INTERVIEW · vizlearn.in/interview/list-versus-tuple-versus-deque.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.