When should I use deque or Counter instead of a list or dict?
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.