Code

Nodes, pointers and two counters

Create playlist.go (see the panel). It is a longish file, but it holds four separate things and they are worth telling apart.

Node and Playlist — the structure itself. Playlist keeps Head, Tail and Len so that Append is O(1): appending to the end needs the tail, and we already have it.

NodeAt — the walk. This is the entire cost of a linked list, which is why every hop gets counted with its own counter.

unlink and insertBefore — the move itself. Both O(1), both nothing but pointer rewrites.

MoveInSlice — the same job on a []Item, so there is something to compare against. Every shift is counted.

Two counters, not one

Splice takes two counters:

func (p *Playlist) Splice(from, to int, walk, ptr *metrics.Counter) bool

That is not tidiness. Collapse them into one and you get a single number and lose the whole lesson: the move is O(1), and getting to it is O(n). Two counters let you see both facts separately.

In step 4 you find out which one dominates.

Why container/list as well

Go's standard library already has a doubly-linked list — container/list. MoveInStdList does the same job through it.

It is here because of a third thread that runs through the course: what an abstraction costs. In lesson 6 you will find the standard library's sort.Search is faster than a hand-written loop. In lesson 13 you will find the standard library's container/heap is slower than a hand-written heap. Same question, opposite answers — and the rule that reconciles them arrives in lesson 13.

For now, just write down the third number.