CPython deque
is implemented as a doubly-linked list of 64-item sized "blocks" (arrays). The blocks are all full, except for the ones at either end of the linked list. IIUC, the blocks are freed when a pop
/ popleft
removes the last item in the block; they are allocated when append
/appendleft
attempts to add a new item and the relevant block is full.
I understand the listed advantages of using a linked list of blocks rather than a linked list of items:
- reduce memory cost of pointers to prev and next in every item
- reduce runtime cost of doing
malloc
/free
for every item added/removed - improve cache locality by placing consecutive pointers next to each other
But why wasn't a single dynamically-sized circular array used instead of the doubly-linked list in the first place?
AFAICT, the circular array would preserve all the above advantages, and maintain the (amortized) cost of pop*
/append*
at O(1)
(by overallocating, just like in list
). In addition, it would improve the cost of lookup by index from the current O(n)
to O(1)
. A circular array would also be simpler to implement, since it can reuse much of the list
implementation.
I can see an argument in favor of a linked list in languages like C++, where removal of an item from the middle can be done in O(1)
using a pointer or iterator; however, python deque
has no API to do this.