container/list.New în Go

Lista dublu înlănțuită din biblioteca standard: tipul list.List și funcția list.New care creează o listă goală.

Sintaxă
import "container/list"

type List struct { /* ... */ }
type Element struct {
    Value any
    // ...
}

func New() *List

Ce face container/list.New

container/list oferă o listă dublu înlănțuită: fiecare element (*list.Element) știe care este elementul dinainte și cel de după. Poți adăuga sau scoate elemente oriunde în listă în timp constant, fără să muți celelalte elemente, cum se întâmplă la un slice.

list.New() returnează un *list.List gol. Și valoarea zero a tipului este gata de folosit: var l list.List merge la fel. Valorile se pun în câmpul Value al fiecărui element, de tip any, așa că la citire faci conversia: e.Value.(string).

În practică, un slice este aproape mereu mai rapid și mai simplu. Lista este utilă când ții pointeri la elemente și le muți des, de exemplu într-un cache LRU (cel mai recent folosit în față, cel mai vechi scos de la coadă).

Ce returnează

New returnează un *list.List gol.

Exemple

Exemplul 1: Creare, adăugare și parcurgere

package main

import (
    "container/list"
    "fmt"
)

func main() {
    l := list.New()
    l.PushBack("Iași")
    l.PushBack("Vaslui")
    l.PushFront("Suceava")
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }

    var goala list.List // valoarea zero e gata de folosit
    goala.PushBack(1)
    fmt.Println(goala.Len())
}

Rezultat

Suceava
Iași
Vaslui
1

Exemplul 2: Cache LRU cu listă și map

Map-ul găsește rapid elementul, lista ține ordinea folosirii. La depășirea capacității se scoate elementul de la coadă (cel mai vechi).

package main

import (
    "container/list"
    "fmt"
)

type Cache struct {
    cap   int
    lista *list.List
    dupa  map[string]*list.Element
}

func (c *Cache) Acceseaza(cheie string) {
    if e, ok := c.dupa[cheie]; ok {
        c.lista.MoveToFront(e)
        return
    }
    c.dupa[cheie] = c.lista.PushFront(cheie)
    if c.lista.Len() > c.cap {
        vechi := c.lista.Back()
        c.lista.Remove(vechi)
        delete(c.dupa, vechi.Value.(string))
    }
}

func main() {
    c := &Cache{cap: 3, lista: list.New(), dupa: map[string]*list.Element{}}
    for _, pagina := range []string{"acasa", "php", "go", "php", "rust"} {
        c.Acceseaza(pagina)
    }
    for e := c.lista.Front(); e != nil; e = e.Next() {
        fmt.Print(e.Value, " ")
    }
    fmt.Println()
}

Rezultat

rust php go

Greșeli frecvente

  • Folosești o listă înlănțuită unde un slice ar ajunge: parcurgerea unei liste este mult mai lentă (elementele sunt împrăștiate în memorie).
  • Uiți conversia la citire: e.Value + 1 nu compilează, pentru că Value este any. Scrie e.Value.(int) + 1.

Comenzi înrudite

  • container/list.List.PushBack: PushBack adaugă o valoare la sfârșitul listei, PushFront la început; ambele returnează elementul nou creat.
  • container/list.List.Front: Front returnează primul element al listei (sau nil dacă e goală), iar Len numărul de elemente; cu Next parcurgi lista.
  • container/list.List.Remove: Scoate elementul e din listă și returnează valoarea lui.
  • container/heap.Init: Funcțiile de bază ale unui heap (coadă de priorități): Init îl organizează, Push adaugă, Pop scoate elementul cel mai mic.