Инициализация карты Голанга - PullRequest
0 голосов
/ 10 января 2019

Насколько я понимаю, типы slice и map во многом схожи в Голанге. Они оба reference (или container) типа. В терминах абстрактных типов данных они представляют массив и ассоциативный массив соответственно.

Однако их поведение совершенно иное.

var s []int
var m map[int]int

Хотя мы можем немедленно использовать объявленный фрагмент (добавить новые элементы или изменить его), мы ничего не можем сделать с вновь объявленной картой. Мы должны вызвать make функцию и явно инициализировать карту. Поэтому, если какая-то структура содержит карту, мы должны написать функцию конструктора для структуры.

Итак, вопрос в том, почему невозможно добавить некоторый синтаксический сахар, а также выделить и инициализировать память при объявлении карты.

Я сделал гугл-вопрос, узнал новое слово «автовивификация», но все еще не смог понять причину.

ДОБАВЛЕНО: Я не говорю о struct literal. Да, вы можете явно инициализировать карту, указав такие значения, как m := map[int]int{1: 1}. Однако, если у вас есть struct:

package main

import (
    "fmt"
)

type SomeStruct struct {
    someField map[int]int
    someField2 []int
}

func main() {
    s := SomeStruct{}
    s.someField2 = append(s.someField2, -1) // OK
    s.someField[0] = -1 // panic: assignment to entry in nil map
    fmt.Println(s)
}

Невозможно использовать структуру сразу (со значениями по умолчанию для всех полей). Нужно создать функцию конструктора для SomeStruct, которая должна явно инициализировать карту.

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Хотя мы можем немедленно использовать объявленный фрагмент (добавить новые элементы или изменить его), мы ничего не можем сделать с вновь объявленной картой. Мы должны вызвать функцию make и явно инициализировать карту. Поэтому, если какая-то структура содержит карту, мы должны написать функцию-конструктор для структуры.

Это не правда. Значение по умолчанию - или, точнее, нулевое значение - для срезов и карт равно nil. Вы можете сделать «то же самое» с картой nil, как вы можете сделать с ломтиком nil. Вы можете проверить длину карты nil, можете индексировать карту nil (результатом будет нулевое значение типа значения карты), например, следующие все работают:

var m map[int]int

fmt.Println(m == nil) // Prints true
fmt.Println(len(m))   // Prints 0
fmt.Println(m[2])     // Prints 0

Попробуйте на игровой площадке Go .

Что вы «чувствуете» больше в отношении среза с нулевым значением, так это то, что вы можете добавлять к нему значения. Это правда, но под капотом будет выделен новый срез, используя точную встроенную функцию make(), которую вам нужно будет вызвать для карты, чтобы добавить в нее записи, и вам придется (пере) назначить возвращенный фрагмент. Таким образом, срез с нулевым значением «не более готов к использованию», чем карта с нулевым значением. append() просто заботится о необходимом (пере) выделении и копировании. У нас может быть «эквивалентная» addEntry() функция, в которую можно передать значение карты и пары ключ-значение, и если переданная карта равна nil, она может выделить новое значение карты и вернуть его. Если вы не вызовете append(), вы не сможете добавить значения к срезу nil, так же как вы не можете добавить записи к карте nil.

Основной причиной того, что нулевым значением для срезов и карт является nil (а не инициализированный срез или карта), является производительность и эффективность. Очень часто значение карты или среза (либо переменное, либо структурное поле) никогда не будет использовано, или не сразу, и поэтому, если они будут размещены при объявлении, это будет пустой тратой памяти (и некоторого процессора) ресурсы, не говоря уже о том, что дает больше работы сборщику мусора. Также, если нулевое значение будет инициализированным значением, оно часто будет недостаточным (например, срез 0-го размера не может содержать какие-либо элементы), и часто оно будет отбрасываться при добавлении в него новых элементов (поэтому первоначальное распределение будет полный отход).

Да, есть случаи, когда вы хотите использовать срезы и карты сразу, в таких случаях вы можете вызвать make() самостоятельно или использовать составной литерал . Вы также можете использовать специальную форму make(), в которой вы указываете (начальную) емкость для карт, избегая будущей реструктуризации внутренних элементов карты (которая обычно требует значительных вычислений). Автоматическое значение, отличное от nil по умолчанию, не может угадать, какая емкость вам потребуется.

0 голосов
/ 10 января 2019

можно! То, что вы ищете:

package main

import "fmt"

func main() {
    v := map[int]int{}

    v[1] = 1
    v[2] = 2

    fmt.Println(v)
}

:= объявлен и назначен, где var просто объявлен.

...