Declaration, initialization and make

Posted by 99naa on Fri, 04 Feb 2022 07:26:40 +0100

concept

map is a reference type and can be declared as follows:

var map1 map[keytype]valuetype
var map1 map[string]int

([keytype] and valuetype allow spaces, but gofmt removes spaces)

When declaring, you don't need to know the length of the map. The map can grow dynamically.

The value of uninitialized map is nil.

Key can be any type. You can use = = or= Operators compare types, such as string, int, float. Therefore, slices and structures cannot be used as keys, but pointer and interface types can. If you want to use a structure as a key, you can provide Key() and Hash() methods, so that you can calculate the unique number or string key through the field of the structure.

Value can be any type; By using empty interface type, we can store any value, but when using this type as a value, we need to make a type assertion first.

The cost of map passing to the function is small: 4 bytes on 32-bit machines and 8 bytes on 64 bit machines, regardless of how much data is actually stored. It is very fast to find the value in the map through key, which is much faster than linear search, but it is still 100 times slower than reading directly from the index of array and slice; So if you care about performance, it is recommended to use slicing to solve the problem.

map can also use function as its own value, which can be used as a branch structure: key is used to select the function to be executed.

If key1 is the key of map1, map1[key1] is the value corresponding to key1, just like the array index symbol (the array can be regarded as a simple form of map, and the key is an integer starting from 0).

The value corresponding to key1 can be set to val1 through the assignment symbol: map1[key1] = val1.

Let v: = map1 [key1] assign the value corresponding to key1 to v; If key1 does not exist in the map, v will be assigned a null value of the value type of map1.

The commonly used len(map1) method can obtain the number of pairs in the map, which is scalable because map pairs can be dynamically added and deleted at run time.

  map01

package main
import "fmt"

func main() {
	var mapLit map[string]int
	//var mapCreated map[string]float32
	var mapAssigned map[string]int

	mapLit = map[string]int{"one": 1, "two": 2}
	fmt.Printf("Map literal at \"two\" is: %d\n", mapLit["two"])
	mapCreated := make(map[string]float32)
	mapAssigned = mapLit

	mapCreated["key1"] = 4.5
	mapCreated["key2"] = 3.14159
	mapAssigned["two"] = 3

	fmt.Printf("Map literal at \"one\" is: %d\n", mapLit["one"])
	fmt.Printf("Map created at \"key2\" is: %f\n", mapCreated["key2"])
	fmt.Printf("Map literal at \"two\" is: %d\n", mapLit["two"])
	fmt.Printf("Map assigned at \"two\" is: %d\n", mapAssigned["two"])
	fmt.Printf("Map literal at \"ten\" is: %d\n", mapLit["ten"])
}

Output

Map literal at "two" is: 2
Map literal at "one" is: 1
Map created at "key2" is: 3.141590
Map literal at "two" is: 3
Map assigned at "two" is: 3
Map literal at "ten" is: 0

mapLit explains how to use map literals: maps can be initialized with the description method of {key1: val1, key2: val2}, just like arrays and structures.

map is of reference type: memory is allocated using the make method.

Initialization of map: var map1 = make(map[keytype]valuetype).

Or abbreviated as: MAP1: = make (map [keytype] ValueType).

mapCreated in the above example is created in this way: mapCreated: = make (map [string] float32).

Equivalent to: mapcreated: = map [string] float32 {}.

mapAssigned is also a reference to mapList. The modification of mapAssigned will also affect the value of mapLit.

Instead of using new, always use make to construct a map

Note that if you mistakenly use new() to allocate a reference object, you will get a null reference pointer, which is equivalent to declaring an uninitialized variable and taking its address:

mapCreated := new(map[string]float32)

Next, when we call: mapCreated["key1"] = 4.5, the compiler will report an error:

invalid operation: mapCreated["key1"] (index of type *map[string]float32).

To illustrate that the value can be of any type, here is a map using func() int as the value:

  map02

package main
import "fmt"

func main() {
    mf := map[int]func() int{
        1: func() int { return 10 },
        2: func() int { return 20 },
        5: func() int { return 50 },
    }
    fmt.Println(mf)
}

Output

map[1:0xe6c960 2:0xe6c980 5:0xe6c9a0]

map capacity

Unlike arrays, maps can be dynamically scaled according to the new key value, so there is no fixed length or maximum limit. But you can also choose to indicate the initial capacity of the map, like this: make(map[keytype]valuetype, cap). For example:

map2 := make(map[string]float32, 100)

When the map grows to the maximum capacity, if a new key value pair is added, the size of the map will automatically increase by 1. Therefore, for the sake of performance, it is better to indicate the capacity of a large map or a map that will expand rapidly, even if you only know about it.

Here is a specific example of map, which maps the scale to the corresponding audio:

noteFrequency := map[string]float32 {
    "C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
    "G0": 24.50, "A0": 27.50, "B0": 30.87, "A4": 440}

Use slice as map value

Since a key can only correspond to one value, and value is an original type, what if a key corresponds to multiple values? For example, when we want to process all processes on a unix machine, we take the parent process (pid as integer) as the key and all child processes (slices composed of pid of all child processes) as the value. This problem can be solved gracefully by defining value as [] int type or other types of slices.

Here are some examples of defining this map:

mp1 := make(map[int][]int)
mp2 := make(map[int]*[]int)