while switch over golang for example elif array for-loop map go

for loop - switch - ¿Es seguro eliminar las teclas seleccionadas del mapa de Golang dentro de un bucle de rango?



if golang (4)

En resumen, sí. Ver respuestas anteriores.

Y también esto, desde here :

ianlancetaylor hizo un comentario el 18 de febrero de 2015
Creo que la clave para entender esto es darse cuenta de que al ejecutar el cuerpo de una instrucción for / range, no hay una iteración actual. Hay un conjunto de valores que se han visto y un conjunto de valores que no se han visto. Al ejecutar el cuerpo, uno de los pares de clave / valor que se ha visto, el par más reciente, se asignó a la (s) variable (s) de la declaración de rango. No hay nada de especial en ese par clave / valor, es solo uno de los que ya se ha visto durante la iteración.

La pregunta que está respondiendo se trata de modificar los elementos del mapa en su lugar durante una operación de range , por lo que menciona la "iteración actual". Pero también es relevante aquí: puede eliminar claves durante un rango, y eso simplemente significa que no las verá más adelante en el rango (y si ya las vio, está bien).

¿Cómo se pueden eliminar las claves seleccionadas de un mapa de Golang? ¿Es seguro combinar delete () con rango, como en el siguiente código?

http://play.golang.org/p/u1vufvEjSw

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) } }


Esto es seguro! También puede encontrar una muestra similar en Effective Go :

for key := range m { if key.expired() { delete(m, key) } }

Y la especificación del lenguaje :

El orden de iteración sobre los mapas no se especifica y no se garantiza que sea el mismo de una iteración a la siguiente. Si las entradas de mapa que aún no se han alcanzado se eliminan durante la iteración , los valores de iteración correspondientes no se producirán. Si se crean entradas de mapa durante la iteración , esa entrada puede producirse durante la iteración o puede omitirse. La elección puede variar para cada entrada creada y de una iteración a la siguiente. Si el mapa es nulo, el número de iteraciones es 0.


La respuesta de Sebastian es precisa, pero quería saber por qué era segura, así que investigué el código fuente del Mapa . Parece que en una llamada para delete(k, v) , básicamente solo establece un indicador (además de cambiar el valor de conteo) en lugar de eliminar el valor:

b->tophash[i] = Empty;

(Empty es una constante para el valor 0 )

Lo que el mapa parece estar haciendo realmente es asignar un número determinado de cubos según el tamaño del mapa, que crece a medida que realiza inserciones a razón de 2^B (desde este código fuente ):

byte *buckets; // array of 2^B Buckets. may be nil if count==0.

Por lo tanto, casi siempre hay más espacios asignados de los que está utilizando, y cuando hace un range sobre el mapa, verifica el valor tophash de cada tophash en ese 2^B para ver si puede omitirlo.

Para resumir, la delete dentro de un range es segura porque los datos están técnicamente todavía allí, pero cuando se comprueba el tophash se ve que puede omitirlo y no incluirlo en la operación de range que esté realizando. El código fuente incluso incluye un TODO :

// TODO: consolidate buckets if they are mostly empty // can only consolidate if there are no live iterators at this size.

Esto explica por qué el uso de la función delete(k,v) realidad no libera memoria, solo la elimina de la lista de depósitos a los que se puede acceder. Si desea liberar la memoria real, tendrá que hacer que no se pueda acceder al mapa completo para que la recolección de elementos no utilizados intervenga. Puede hacerlo utilizando una línea como

map = nil


Me preguntaba si podría ocurrir una pérdida de memoria. Entonces escribí un programa de prueba:

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) }

Parece que GC libera la memoria. Entonces está bien.