SoFunction
Updated on 2025-03-05

Solve the problem of unstable results of Golang map range traversal

Less gossip, this article mainly wants to introduce a common problem in Golang development. However, this problem is often easily trapped for beginners.

question

When I was writing a piece of code, I used Golang's map data structure, and the purpose was to use map to cache counting results. Simply put, the keys of map are also integers and are stored in incremental order. My initial idea was to output values ​​in an orderly manner according to the keys stored in the map after the statistics were finished. However, when I run the program, the result is not what I want, and there is a certain probability that the result will be different.

Problem code

func sortByBits(arr []int) []int {
  var bitmap = make(map[int][]int, 0)
  // map cache  for i := 0; i < len(arr); i++ {
    bits := calBits(arr[i])
    if _, ok := bitmap[bits]; !ok {
      tmp := make([]int, 0)
      tmp = append(tmp, arr[i])
      bitmap[bits] = tmp
    } else {
      bitmap[bits] = append(bitmap[bits], arr[i])
      for j := len(bitmap[bits]) - 1;j > 0; j-- {
        if bitmap[bits][j] < bitmap[bits][j - 1] {
          bitmap[bits][j], bitmap[bits][j - 1] = bitmap[bits][j - 1], bitmap[bits][j]
        }
      }
    }
  }
  // Output  var res []int
  for _, value := range bitmap {
    res = append(res, value...)
  }
  return res
}
func calBits(n int) int {
  sum := 0
  for n > 0 {
    if n & 1 == 1 {
      sum++
    }
    n = n >> 1
  }
  return sum
}

When I discovered this problem, I used a two-dimensional array to replace the map and remodel the code as follows:

Revise the code

func sortByBits(arr []int) []int {
  var bitmap = make([][]int, 0, 10000)
  // map cache  for i := 0; i < len(arr); i++ {
    bits := calBits(arr[i])
    if len(bitmap) <= bits {
      length := bits - len(bitmap) + 1
      for j := 0; j < length; j++ {
        tmp := make([]int, 0)
        bitmap = append(bitmap, tmp)
      }
    }
      
    if len(bitmap[bits]) == 0 {
      bitmap[bits] = append(bitmap[bits], arr[i])
    } else {
      bitmap[bits] = append(bitmap[bits], arr[i])
      for j := len(bitmap[bits]) - 1;j > 0; j-- {
        if bitmap[bits][j] < bitmap[bits][j - 1] {
          bitmap[bits][j], bitmap[bits][j - 1] = bitmap[bits][j - 1], bitmap[bits][j]
        }
      }
    }
  }
  // Output  var res []int
  for _, value := range bitmap {
    res = append(res, value...)
  }
  return res
}
func calBits(n int) int {
  sum := 0
  for n > 0 {
    if n & 1 == 1 {
      sum++
    }
    n = n >> 1
  }
  return sum
}

The code is simple, and the problem is simple. The principle is that Golang's map key output results are random, which is a feature of the language itself, or a "pit". As a developer, you must master the most basic characteristics of the language in order to develop the most robust programs.

Supplement: golang if _,ok:=range map; ok determines whether the key is in the map

Since golang does not provide a method of judging whether the item is in the array, if this judgment is frequently used in the program, the array can be converted into a map with the members of the array as the key and then used the above method to make judgments, which will improve the efficiency of judgment.

Judgment method example code

if _, ok := map[key]; ok {
//exist}

If you have a for loop every time, it will affect performance!

The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.