Q&A library Python

When should I use deque or Counter instead of a list or dict?

Python Intermediate collectionscounterdeque

Short answer

A deque adds and removes from both ends in constant time and can cap its own length, while a list is slow at the front. Counter tallies items and supports arithmetic between tallies.

The short version

Use a deque when you add or remove items at the front of a sequence, because a list has to shuffle every element to do that. Use a Counter when you are tallying, because it counts in one pass and supports arithmetic between tallies. Both come from the collections module.

Why a list is slow at the front

Appending to the end of a list is fast, but inserting or removing at the start requires moving every other element along, which gets slow as the list grows.

from collections import deque

queue = deque([1, 2, 3])
queue.appendleft(0)     # fast
queue.popleft()         # fast

A list's insert(0, x) and pop(0) are the operations to avoid on anything large. If your data structure is a queue (first in, first out), a deque is the right choice.

The sliding window trick

A deque with maxlen keeps only the most recent items, discarding the oldest automatically, which is a neat way to hold a rolling window.

recent = deque([1, 2, 3], maxlen=3)
recent.append(4)
print(list(recent))   # [2, 3, 4] , the 1 dropped off the front

This is ideal for keeping the last N events without any manual trimming.

Counter for tallying

from collections import Counter

counts = Counter(["a", "a", "b"])
print(counts.most_common(1))   # [('a', 2)]

most_common returns the top entries already sorted, which saves sorting the dictionary yourself.

Counter arithmetic

Counters subtract and add, which is a genuinely useful and little-known feature.

Counter("aab") - Counter("a")   # Counter({'a': 1, 'b': 1})

This makes it easy to compare two tallies, for example to see what changed between two runs, without writing a loop over the keys.

A missing key gives zero

Unlike a plain dictionary, asking a Counter for an item it has never seen returns 0 rather than raising, which removes a lot of defensive checking.

counts["z"]   # 0, not a KeyError

Choosing between them and the built-ins

Use a plain list for a general ordered collection you mostly append to and index into. Use a deque when the front matters or you want a fixed-length window. Use a plain dict for general key-value mapping, and a Counter when the values are counts. Reaching for the specialised type makes the intent obvious and usually makes the code faster too, which is the whole point of the collections module.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project