Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

XDM, I’m Nezha

In daily development work, we are not unfamiliar with the map data structure. In Golang, of course, there is map

About the use of map, there are a lot of things to pay attention to, if not clear, these matters, the key time may step on the pit, let’s exercise together

1 Remember to initialize using map

Write a demo

  • Define a map[int] variable myMap of type int, not initialized
  • We can read the value of myMap, which defaults to zero
  • However, if we write values into an uninitialized myMap, the program will panic. Remember not to step on the pit here
func main(a){

	var myMap map[int]int
	fmt.Println("myMap[1] == ",myMap[1])}Copy the code

Program running effect:

# go run main.go
myMap[1] ==   0
Copy the code

Add write operations to code:

func main(a){

	var myMap map[int]int
	fmt.Println("myMap[1] == ",myMap[1])

	myMap[1] = 10

	fmt.Println("myMap[1] == ",myMap[1])}Copy the code

Program running effect:

# go run main.go
myMap[1] ==   0
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /home/admin/golang_study/later_learning/map_test/main.go:20 +0xf3
exit status 2
Copy the code

As expected, the program panic, we need to be very careful in the actual work, the code should be in awe of the heart

2 Map traversal is unordered

  • Define a map[int] map of int type and initialize 5 numbers
func main(a) {
	myMap := map[int]int{
		1: 1.2: 2.3: 3.4: 4.5: 5}

	for k := range myMap {
		fmt.Println(myMap[k])
	}
}
Copy the code

Program running effect:

# go run main.goOne, two, three, four, five# go run main.go5, 1, 2, 3, 4# go run main.go3, 4, 5, 1, 2Copy the code

Run the above code three times and the results will be different three times. Of course, it is possible that the results will be in the same order three times

Because the map in GO is implemented based on a hash table, traversal is out of order

If we need to empty the map, we can just set the corresponding map variable to nil, for example

myMap = nil
Copy the code

3 Maps can also be two-dimensional

Maps can also be two-dimensional, like arrays, or even multi-dimensional, depending on our needs

However, we should note that the definition is just like a two-dimensional array, but the specific use is still different

We can manipulate two-dimensional arrays like this

func main(a) {
	myMap := map[int]map[string]string{}
	myMap[0] = map[string]string{
		"name":"xiaomotong"."hobby":"program",
	}
	fmt.Println(myMap)
}
Copy the code

Program running effect:

# go run main.go
map[0:map[name:xiaomotong hobby:program]]
Copy the code

We can’t manipulate two-dimensional arrays like this

func main(a) {
	myMap := map[int]map[string]string{}
	myMap[0] ["name"] = "xiaomotong"
	myMap[0] ["hobby"] = "program"

	fmt.Println(myMap)
}
Copy the code

Program running effect:

# go run main.go
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /home/admin/golang_study/later_learning/map_test/main.go:17 +0x7f
exit status 2
Copy the code

The reason is simple, and the panic log in the program has already explained the reason

MyMap [0] key is 0, but the value is map[string]string, which needs to be initialized to do write operations

If you want to write it this way, it’s easy, just initialize it

func main(a) {
	myMap := map[int]map[string]string{}
	myMap[0] = map[string]string{}
    
	myMap[0] ["name"] = "xiaomotong"
	myMap[0] ["hobby"] = "program"

	fmt.Println(myMap)
}
Copy the code

4 Use this method to obtain the map key

At work, there is a way for us to get all the keys of a map. In this case, how do we get all the keys of a map? XDM who has been exposed to reflection will definitely say, this is very easy.

func main(a) {
	myMap := map[int]int{
		1: 1.2: 2.3: 3.4: 4.5: 5}

	myKey := reflect.ValueOf(myMap).MapKeys()

	for v :=range myKey{
		fmt.Println(v)
	}
}
Copy the code

Run the program go run main.go and the result is as follows:

But as we all know, Reflection in Golang is really neat to write, but it’s really inefficient, so it’s best to use the following method

func main(a) {
	myMap := map[int]int{
		1: 1.2: 2.3: 3.4: 4.5: 5}

	myKey := make([]int.0.len(myMap))

	for k :=range myMap{
		myKey = append(myKey,myMap[k])
	}
	fmt.Println(myKey)
}
Copy the code

In this encoding mode, the capacity of myKey slice is set in advance to be the same as the length of map. In this case, when adding keys to myKey in the future, there will be no need for slice expansion

Program running effect:

# go run main.go
[2 3 4 5 1]
Copy the code

And we can see that the key that comes out is not in order either

5 Map is concurrency insecure, sync.map is

Finally, let’s simulate and verify that Golang’s map is not secure

Simulation map is not safe demo, need to open more coroutines to simulate the effect, the experiment, I simulated open 50,000 coroutines

type T struct {
	myMap map[int]int
}

func (t *T) getValue(key int) int {
	return t.myMap[key]
}

func (t *T) setValue(key int, value int) {
	t.myMap[key] = value
}

func main(a) {

	ty := T{myMap: map[int]int{}}

	wg := sync.WaitGroup{}
	wg.Add(50000)
	for i := 0; i < 50000; i++ {

		go func(i int) {
			ty.setValue(i, i)
			fmt.Printf("get key == %d, value == %d \n", i, ty.getValue(i))
			wg.Done()
		}(i)
	}

	wg.Wait()
	fmt.Println("program over !!")}Copy the code

The following information will be displayed when the program changes:

# go run main.go
fatal error: concurrent map writes
...
Copy the code

If you must use a map, you can also add a mutex to solve the problem

We just need to modify the above code, the struct’s defined position, and the function that sets the value

type T struct {
	myMap map[int]int
	lock sync.RWMutex
}

func (t *T) setValue(key int, value int) {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.myMap[key] = value
}
Copy the code

Go >> map.log to print the output value of the program to a file

After the program runs, it can be seen that the data corresponding to the real printed key is indeed 5000 lines, no problem

Golang provides a concurrency safe map, sync.map. Golang also provides a concurrency safe map, sync.map

Sync.map’s implementation mechanism, in a nutshell, is its own locking, thus controlling concurrency security

Ok, enough for today, language is good language, tools are good tools, we need to use them in order to play their value, without everything is useless

Welcome to like, follow and favorites

Friends, your support and encouragement, I insist on sharing, improve the quality of the power

All right, that’s it for this time

Technology is open, our mentality, should be more open. Embrace change, live in the sun, and strive to move forward.

I am Nezha, welcome to like, see you next time ~