¿Por qué no puede ir iterar los mapas en el orden de inserción?

Tengo una barra de navegación como mapa:

var navbar = map[string]navbarTab{ } 

Donde navbarTab tiene varias propiedades, elementos secundarios, etc. Cuando bash renderizar la barra de navegación (con for tabKey := range navbar ) aparece en orden aleatorio. Soy consciente de que el range ordena al azar cuando se ejecuta, pero parece que no hay forma de obtener una lista ordenada de claves o iterar en el orden de inserción.

Parece ridículo que esto no sea posible.

El enlace del patio de recreo está aquí: http://play.golang.org/p/nSL1zhadg5 aunque parece que no muestra el mismo comportamiento.

¿Cómo puedo iterar sobre este mapa sin romper el orden de inserción?

Los mapas de Go no mantienen el orden de inserción; tendrás que implementar este comportamiento tú mismo.

Ejemplo:

 type NavigationMap struct { m map[string]navbarTab keys []string } func NewNavigationMap() *NavigationMap { ... } func (n *NavigationMap) Set(k string, v navbarTab) { nm[k] = v n.keys = append(n.keys, k) } 

Este ejemplo no está completo y no cubre todos los casos de uso (por ejemplo, actualizar el orden de inserción en claves duplicadas).

Si su caso de uso incluye volver a insertar la misma clave varias veces (esto no actualizará el orden de inserción para la clave k si ya estaba en el mapa):

 func (n *NavigationMap) Set(k string, v navbarTab) { _, present := nm[k] nm[k] = v if !present { n.keys = append(n.keys, k) } } 

Elija la cosa más simple que satisfaga sus requisitos.

El concepto general de la estructura de datos del mapa es que se trata de una colección de pares clave-valor. “Ordenado” u “ordenado” no se menciona en ninguna parte.

Definición de Wikipedia:

En informática, una matriz asociativa , mapa , tabla de símbolos o diccionario es un tipo abstracto de datos compuesto por una colección de pares (key, value) , de modo que cada clave posible aparece una sola vez en la colección.

El mapa es una de las estructuras de datos más útiles en informática, por lo que Go lo proporciona como un tipo incorporado. Sin embargo, la especificación del lenguaje solo especifica un mapa general ( tipos de mapas ):

Un mapa es un grupo desordenado de elementos de un tipo, llamado tipo de elemento, indexado por un conjunto de claves únicas de otro tipo, llamado tipo de clave. El valor de un mapa no inicializado es nil .

Tenga en cuenta que la especificación del lenguaje no solo excluye las palabras “ordenadas” u “ordenadas” , sino que establece explícitamente lo contrario: “desordenado” . ¿Pero por qué? Porque esto otorga una mayor libertad al tiempo de ejecución para implementar el tipo de mapa. La especificación del lenguaje permite utilizar cualquier implementación de mapa, como el mapa Hash , el mapa de árbol , etc. Tenga en cuenta que las versiones actuales (y anteriores) de Go utilizan una implementación de mapa hash, pero no es necesario que lo sepa para usarlo.

La publicación del blog Ir a los mapas en acción es una lectura obligada con respecto a esta pregunta.

Antes de ir a 1, cuando no se cambiaba un mapa, el tiempo de ejecución devolvía las claves en el mismo orden cuando iteraba sobre sus claves / entradas varias veces. Tenga en cuenta que este orden podría haber cambiado si el mapa se modificó, ya que la implementación podría necesitar una repetición para acomodar más entradas. Las personas comenzaron a confiar en el mismo orden de iteración (cuando el mapa no se modificó), por lo que a partir de Go 1 las aleatorias de tiempo de ejecución mapean la orden de iteración a propósito para llamar la atención de los desarrolladores que el orden no está definido y no se puede confiar en él .

Que hacer entonces?

Si necesita un conjunto de datos ordenado (ya sea una colección de pares clave-valor o cualquier otra cosa) ya sea por orden de inserción o por orden natural definido por el tipo de clave o un orden arbitrario, el map no es la elección correcta. Si necesita un orden predefinido, las rebanadas (y las matrices) son su amigo. Y si necesita poder buscar los elementos con una clave predefinida, también puede construir un mapa a partir del sector para permitir una búsqueda rápida de los elementos mediante una tecla .

O construyes primero el map y luego un corte en el orden correcto, o el corte primero y luego construyes un map depende de ti.

La publicación del blog Go maps in action mencionada anteriormente tiene una sección dedicada a la orden de iteración :

Cuando se itera sobre un mapa con un bucle de rango, el orden de iteración no se especifica y no se garantiza que sea el mismo de una iteración a la siguiente. Desde Go 1, el tiempo de ejecución aleatoriza el orden de iteración del mapa, ya que los progtwigdores se basaron en el orden de iteración estable de la implementación anterior. Si necesita una orden de iteración estable, debe mantener una estructura de datos separada que especifique esa orden. Este ejemplo usa un segmento de claves ordenadas por separado para imprimir una map[int]string en el orden de las teclas:

 import "sort" var m map[int]string var keys []int for k := range m { keys = append(keys, k) } sort.Ints(keys) for _, k := range keys { fmt.Println("Key:", k, "Value:", m[k]) } 

PD:

… aunque parece no exhibir el mismo comportamiento.

Aparentemente ve el “mismo orden de iteración” en el área de juegos Go porque las salidas de las aplicaciones / códigos en el área de juegos Go están en la memoria caché . Una vez que se ejecuta un código nuevo pero único, su salida se guarda como nueva. Una vez que se ejecuta el mismo código, la salida guardada se presenta sin ejecutar el código nuevamente. Así que, básicamente, no es el mismo orden de iteración lo que ves, es exactamente el mismo resultado sin ejecutar de nuevo el código.