What issues should be paid attention to when using
The keys of a Map must be comparable types, such as integers, strings, and pointers, but types such as slices, functions and structures are incomparable and cannot be used as keys.
The elements in the Map are unordered, which means that the order of elements may change randomly when traversing the Map.
The capacity of the Map is dynamically changed, and it automatically adjusts the capacity to accommodate new elements.
If you use an uninitialized map, it will cause a runtime error. The Map needs to be initialized using the make() function.
Map is not safe in concurrent environments. If you need to use Map in a concurrent environment, you need to use the lock mechanism provided in the sync package to protect the Map.
How is the capacity expansion implemented
In Go, the internal implementation of Map is based on a hash table, so when the number of key-value pairs in the Map increases, the Map will automatically expand to provide more storage space. Here are the specific steps for expanding Go Map:
- When the number of elements in a map exceeds the product of the load factor and hash table capacity, the map will trigger the expansion operation. In Go, the load factor defaults to 6.5.
- Go Map creates a new hash table when expanding and rehashs the original key-value pairs into the new hash table. To reduce hash conflict, the capacity of the new hash table is twice as large as the original, and the capacity must be at a power of 2. That is, after each expansion is calculated according to the rules, in order to align memory and reduce memory fragmentation, it will be rounded upwards based on a constant number.
- During the rehash process, Go Map assigns the original key-value pair to the corresponding position in the new hash table based on the hash value. If the hash values of two key-value pairs are the same, they will be processed using a chained hash table and placed in the same bucket.
- Once all key-value pairs have been rehashed into the new hash table, Go Map will release the original hash table and use the new hash table as the internal storage structure of the Map.
Notice:Since the expansion operation may lead to a large number of rehash operations, it may have a certain performance impact when expanding. To avoid frequent expansion, you can specify a larger capacity when creating a map, orManual call ()to trigger the garbage collection operation to free unused memory.
3. Can the panic of Map be recovered?
cannot, Concurrent reading and writing Map is also a very easy problem to encounter
if &hashWriting != 0 { throw("concurrent map read and map write") }
()
The function cannot recover if it throws an exception.
4. Is there any other solution to using Map in concurrently besides locking?
In addition to locking, other common solutions for Go concurrent use of Map include using and using concurrently secure third-party Map libraries.
1. Usage is a concurrently secure map newly added to Go 1.9. Its read and write operations are concurrently secure and there is no need to add locks. The example code used is as follows:
var m ("name", "Programmer Zhurong") value, ok := ("name") if ok { (value) } // Output programmer Zhu Rong
2. Use concurrent-safe third-party map libraries. In addition to using , you can also use other third-party concurrent-safe Map libraries, such as concurrent-map, ccmap, etc. These libraries are used in a similar way to the Go standard library’s Map, but they all provide more efficient and reliable concurrency security assurance.
Notice:Using concurrently secure third-party Map libraries can lead to additional dependencies and complexity, so careful evaluation is required to be made for use
5. What is the difference between locking
The difference between using locks is that locking and unlocking operations are not required during concurrent access. In contrast, using locks requires explicit locking and unlocking during concurrent access to avoid race conditions and data race issues.
When using locks, race conditions must be avoided by adding multiple goroutines when accessing the same piece of data at the same time. This means that only one goroutine can access the data, and that other goroutines can access the data after the goroutine has completed its work. This way ensures data consistency, but locking can bring additional overhead and may affect performance in high concurrency situations.
In contrast, more advanced algorithms are used to avoid race conditions and data competition problems without explicit locking and unlocking. When multiple goroutines are accessed simultaneously, it automatically allocates different segments to store data, and each segment has its own read and write lock to avoid race conditions. This method can improve concurrency performance, reduce overhead, and avoid problems such as deadlock.
6. Map query time complexity
The underlying implementation of Map in Go uses a hash table, so its query time complexity is O(1), and the worst case is O(n).
7. What is the strategy of Map Rehash? When will the chance happen? Rehash
When the number of elements in the hash table reaches a certain threshold, the expansion operation of the hash table will be triggered, that is, rehash.
The hash table implementation in the Go standard library triggers the rehash operation if:
- When the number of elements in the hash table exceeds 2/3 of the hash table capacity, the capacity expansion operation will be triggered. At this point, the capacity of the hash table will double, and all elements in the hash table will be rehashed into a new slot.
- When the number of elements in the hash table is too small and the capacity of the hash table is too large, a shrinking operation will be triggered. At this time, the capacity of the hash table will be halved, and all elements in the hash table will be rehashed into a new slot.
- When the hash table detection sequence is too long, a rearrangement operation will be triggered. At this time, the elements in the hash table will not be rehashed, but their storage location will change, thereby reducing clustering and improving the performance of the hash table.
When performing a rehash operation, the hash table creates a new array to store the rehashed elements, and then copy the elements from the old array into the new array one by one. Since the rehashing process is time-consuming, the hash table implementation in the Go standard library adopts an incremental rehash strategy, which only processes a part of the elements when expanding and shrinking, avoiding performance degradation due to excessive elements at one time. Specifically, the strategy of incremental rehash is:
- Set the capacity of the new array to twice or half of the old array and increase the increment counter of the hash table by one.
- When operating on the hash table, if the value of the incremental counter reaches a threshold, the incremental rehash operation will be started, copying a portion of the elements from the old array into the new array, and recalculating the hash values of these elements.
- After completing an incremental rehash operation, the increment counter of the hash table will be cleared.
Through the incremental rehash strategy, the hash table implementation in the Go standard library can ensure a short rehash time while avoiding performance degradation due to excessive elements at one time.
8. What will Map Rehash affect? What will the hash result be affected
rehash operation will affectMap
performance. Because the hash value of the key is recalculated, the rehash operation consumes a certain amount of computing resources. In addition, during the rehash process, all key-value pairs of the original hash table need to be copied to the new hash table, so the rehash operation will also consume a certain amount of memory space and time.
The rehash operation will not directly affect the hash result. The hash result is calculated by the hash function, andMap
The number of elements in it has nothing to do with the layout. The rehash operation only affects the layout of the hash table, that is, the position of each key in the hash table will change, but the hash value of each key will not change.
9. How to migrate elements stored in old buckets during Map Rehash
The rehash operation creates a new hash table while leaving the old hash table unchanged. It then iterates over each bucket in the old hash table in turn, copying all key-value pairs in the bucket into the corresponding bucket in the new hash table. When traversing each bucket, if the elements in the bucket have been deleted, those elements are skipped.
When iterating over to a bucket,Map
The implementation will first acquire the mutex of the bucket in the old hash table to ensure there will be no concurrent access issues when copying elements. It then iterates through all key-value pairs in the bucket, and for each key-value pair it calculates the position of the key in the new hash table and inserts the key-value pair into the corresponding bucket. During the insertion process, if the corresponding bucket in the new hash table already exists, the new element will be inserted at the end of the linked list of the bucket.
Throughout the rehash process, the new hash table remains empty until all elements are copied. After the copy is complete,Map
The implementation will replace the old hash table with the new hash table and free up the memory space occupied by the old hash table. so,Map
All elements in are moved to the new hash table.
It should be noted that if there are concurrent accesses during the rehash processMap
, then race conditions may occur. Therefore, the Go languageMap
Not thread-safe. If you need to access in multiple goroutinesMap
, you need to use mutexes or other concurrent security mechanisms to protect access
10. The Load() method process
func (m *Map) Load(key any) (value any, ok bool) { read, _ := ().(readOnly) e, ok := [key] if !ok && { () // Avoid reporting a spurious miss if got promoted while we were // blocked on . (If further loads of the same key will not miss, it's // not worth copying the dirty map for this key.) read, _ = ().(readOnly) e, ok = [key] if !ok && { e, ok = [key] // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. () } () } if !ok { return nil, false } return () }
visibleLoad
The function reads the element directly from the underlying layer and returns if it exists, regardless of whether there is a write operation at this time. becauseThe data is read through atomic operations within the function.
So when the key exists,Load
Functions will not be locked.Stone
When an element exists, it will not be locked, but will beModify the value of the element.
soLoad
,Store
Locking will only be added when the key does not exist, and will not be added in other situations. Therefore, it is suitable for scenarios where more reads, less writes, and is very fast.
This is the end of this article about the collection of ten frequently asked questions for Go map interviews. For more related Go map content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!