Jak usunąć wybrane klucze z mapy? Czy łączenie delete()
z zakresem jest bezpieczne , jak w kodzie poniżej?
package main
import "fmt"
type Info struct {
value string
}
func main() {
table := make(map[string]*Info)
for i := 0; i < 10; i++ {
str := fmt.Sprintf("%v", i)
table[str] = &Info{str}
}
for key, value := range table {
fmt.Printf("deleting %v=>%v\n", key, value.value)
delete(table, key)
}
}
dictionary
for-loop
go
Everton
źródło
źródło
func (a T) expired() bool
interfejs. Na potrzeby tego przykładu możesz spróbować:m := make(map[int]int)
/* populate m here somehow */
for key := range (m) {
if key % 2 == 0 { /* this is just some condition, such as calling expired */
delete(m, key);
}
}
Odpowiedź Sebastiana jest dokładna, ale chciałem wiedzieć, dlaczego jest to bezpieczne, więc zagłębiłem się w kod źródłowy Mapy . Wygląda na to
delete(k, v)
, że w wywołaniu do , w zasadzie po prostu ustawia flagę (a także zmienia wartość zliczania) zamiast faktycznie usuwać wartość:(Pusta jest stałą dla wartości
0
)Wydaje się, że mapa faktycznie przydziela określoną liczbę segmentów w zależności od rozmiaru mapy, która rośnie w miarę wykonywania wstawiania w tempie
2^B
(z tego kodu źródłowego ):byte *buckets; // array of 2^B Buckets. may be nil if count==0.
Więc prawie zawsze jest przydzielonych więcej zasobników niż używasz, a kiedy przeglądasz
range
mapę, sprawdza tętophash
wartość każdego zasobnika w tym,2^B
aby zobaczyć, czy może ją pominąć.Podsumowując, element
delete
wewnątrz arange
jest bezpieczny, ponieważ dane technicznie wciąż tam są, ale kiedy sprawdzatophash
, widzi, że może go po prostu pominąć i nie uwzględniać go w żadnejrange
wykonywanej operacji. Kod źródłowy zawiera nawetTODO
:// TODO: consolidate buckets if they are mostly empty // can only consolidate if there are no live iterators at this size.
To wyjaśnia, dlaczego użycie tej
delete(k,v)
funkcji w rzeczywistości nie zwalnia pamięci, po prostu usuwa ją z listy zasobników, do których masz dostęp. Jeśli chcesz zwolnić rzeczywistą pamięć, musisz uczynić całą mapę nieosiągalną, aby wkroczyło czyszczenie pamięci. Możesz to zrobić za pomocą linii takiej jakmap = nil
źródło
Zastanawiałem się, czy może dojść do wycieku pamięci. Napisałem więc program testowy:
package main import ( log "github.com/Sirupsen/logrus" "os/signal" "os" "math/rand" "time" ) func main() { log.Info("=== START ===") defer func() { log.Info("=== DONE ===") }() go func() { m := make(map[string]string) for { k := GenerateRandStr(1024) m[k] = GenerateRandStr(1024*1024) for k2, _ := range m { delete(m, k2) break } } }() osSignals := make(chan os.Signal, 1) signal.Notify(osSignals, os.Interrupt) for { select { case <-osSignals: log.Info("Recieved ^C command. Exit") return } } } func GenerateRandStr(n int) string { rand.Seed(time.Now().UnixNano()) const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))] } return string(b) }
Wygląda na to, że GC zwalnia pamięć. Więc jest w porządku.
źródło
Krótko mówiąc, tak. Zobacz poprzednie odpowiedzi.
A także to, stąd :
Pytanie, na które odpowiada, dotyczy modyfikacji elementów mapy na miejscu podczas
range
operacji, dlatego wspomina o „bieżącej iteracji”. Ale ma to również znaczenie tutaj: możesz usuwać klucze w zakresie, a to po prostu oznacza, że nie zobaczysz ich później w zakresie (a jeśli już je widziałeś, to w porządku).źródło