Technology Sharing

【Go series】 array, slice and map

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Connecting the Past and the Future

In our previous article, we introduced if and for. Let's practice how to use them.continueStatement to calculate the sum of even numbers within 100. As we write the code,continueThe statement will help us skip certain unwanted iterations, for example, in this case, we will skip all the odd numbers.

  1. sum := 0
  2. for i := 1; i < 100; i++{
  3. if i & 1 == 0 {
  4. continue
  5. }
  6. sum += i
  7. }
  8. fmt.Println("the sum is",sum)

Start learning

In programming, we often need to process a group of elements of the same type, and these element collections are represented by specific data structures in the Go language. Today, I will introduce several collection types in Go in detail: array, slice, and map.

Arrays

First, let's start with arrays. An array is the most basic data structure in Go. It is a fixed-length sequence of elements of the same type. Once an array is declared, its length cannot be changed. The declaration and initialization of an array is very simple.

Declaring an Array

When declaring an array, you need to specify the array type and the array length. Here is how to declare an array:

var arrayName [arrayLength]elementType

For example, to declare an integer array of length 5:

var numbers [5]int
Initializing an Array

You can initialize arrays in a number of ways:

  • Using literal initialization:
var numbers = [5]int{1, 2, 3, 4, 5}
  • use:=Short statement:
numbers := [5]int{1, 2, 3, 4, 5}
  • Automatically infer array length:
numbers := [...]int{1, 2, 3, 4, 5}
  • Specify index initialization:
numbers := [5]int{0: 1, 4: 5}

Characteristics of arrays

  • Fixed length: The length of an array is determined when it is declared and cannot be changed afterwards.
  • Elements of the same type: An array can only contain elements of the same type.
  • Continuous memory allocation: Array elements are allocated contiguously in memory, which makes accessing array elements very efficient.

Accessing array elements

You can access elements in an array by their index, starting at 0:

value := numbers[2] // 获取索引为 2 的元素

Iterating over an array

you can useforLoop through all elements in an array:

  1. for i, value := range numbers {
  2. fmt.Printf("Index: %d, Value: %dn", i, value)
  3. }

The length of the array

You can use the built-inlenFunction to get the length of an array:

length := len(numbers)

Zero value of an array

If an array is not explicitly initialized, its elements are automatically set to the zero value for its type. For example, the zero value for an array of integer type is 0:

var numbers [5]int // 所有元素都是 0

Multidimensional Arrays

Go also supports multidimensional arrays. The following is an example of declaring and initializing a 2x3 integer array:

  1. var matrix [2][3]int
  2. matrix = [2][3]int{{1, 2, 3}, {4, 5, 6}}

Limitations of Arrays

Since arrays are fixed length, this may not be very flexible in some cases. If you need a variable length collection, you can use slices.

slice

Next is the slice, which is a more flexible built-in type that can be thought of as a dynamic array. The length of the slice is variable, and it is created based on the array, providing more convenience. Here is how to declare and initialize a slice:

  1. s := make([]int, 3) // 创建一个长度为3的整型切片
  2. s[0] = 1 // 切片元素赋值
  3. s[1] = 2
  4. s[2] = 3
  5. s = append(s, 4) // 向切片追加元素

In Go, although arrays and slices are both used to store a series of elements of the same type, they differ significantly in memory allocation, size variability, and usage. The following are the main differences between arrays and slices:

Size variability

  • Arrays: The size of an array is determined when it is declared and cannot be changed afterwards. The size of an array is part of its type, so[3]intand[4]intAre different types.
  • slice: Slices are dynamic and can grow or shrink at runtime. The size of a slice is not part of its type, so[]intIs the common type for all integer slices.

Memory allocation

  • Arrays: Arrays are value types, and when an array is passed as a function parameter, a copy of its value is passed. This means that modifications to the array inside the function will not affect the original array.
  • slice: A slice is a reference type, which contains a pointer to the underlying array, the length of the slice, and the capacity. When a slice is passed as a function parameter, a copy of the pointer is passed, so modifications to the slice inside the function will affect the original slice.

initialization

  • Arrays: When you initialize an array, you must specify its size and you can immediately assign element values.
  • slice: Slices can be passed literals,makefunction or by slicing from an array, without specifying the size.

Sample Code

The following are examples of array and slice initialization:

  1. // 数组
  2. var arr [3]int = [3]int{1, 2, 3}
  3. // 切片
  4. var slice []int = []int{1, 2, 3}
  5. // 或者使用 make 函数
  6. slice := make([]int, 3)

Functional Differences

  • Arrays: Since the size is fixed, the size of the array is known at compile time, which makes it possible to allocate memory for the array on the stack and the time complexity of accessing array elements is O(1).
  • slice: Slices provide more flexibility and can be usedappendFunctions add elements, or obtain subslices through slicing operations. The underlying array of the slice may be allocated on the heap, and the time complexity of accessing slice elements is also O(1), butappendMay cause reallocation of the underlying array, which is typically an O(n) operation.

Mapping

Finally, let's look at maps. Maps are associative arrays in Go that map keys to values. Map keys can be any type supported by the equality operator, such as integers, floating point numbers, strings, pointers, interfaces (as long as the values ​​contained in the interface are comparable), structures, and arrays. Map values ​​can be of any type.

Declaration and Initialization

Declaring a map

The syntax for declaring a map is as follows:

var mapName map[keyType]valueType

For example, to declare a map with keys of string type and values ​​of integer type:

var scores map[string]int
Initialize the map

After declaring the map, you need to passmakefunction to initialize it so that it can be used:

scores = make(map[string]int)

Alternatively, you can use a short declaration and initialization:

scores := make(map[string]int)

It is also possible to use literal initialization at the same time as declaration:

  1. scores := map[string]int{
  2. "alice": 90,
  3. "bob": 85,
  4. "charlie": 88,
  5. }

Features of map

  • Key uniqueness: In a map, each key is unique, if you try to insert an existing key, it will update the value corresponding to that key.
  • Disorder: Map is unordered, and the order of elements may be different each time you iterate over the map.
  • Dynamic Size: The size of the map is dynamic, you can add or remove key-value pairs as needed.
  • Reference Types: map is a reference type. When you pass a map to a function, you are actually passing a pointer to the underlying data structure.

Operation map

Adding Elements
scores["alice"] = 90
Get elements
value := scores["alice"]

If the key does not exist, the zero value for that value type is returned.

Check if a key exists

You can use the comma-ok idiom to check if a key exists in the map:

  1. value, exists := scores["alice"]
  2. if exists {
  3. // 键存在
  4. } else {
  5. // 键不存在
  6. }
Deleting an element

usedeleteThis function can be used to remove a key-value pair from a map:

delete(scores, "alice")

If the key does not exist,deleteThe function does nothing.

Iterating over a map

useforThe loop can iterate over all key-value pairs in the map:

  1. for key, value := range scores {
  2. fmt.Printf("%s: %dn", key, value)
  3. }

Zero value of map

The zero value of the map isnil.onenilMaps have no underlying data structure and cannot have elements added to them. Before adding elements to a map, you must usemakeInitialize it.

The length of the map

You can use the built-inlenFunction to get the number of key-value pairs in the map:

length := len(scores)

The key type of the map

The keys of a map can be of any comparable type, such as integers, floating-point numbers, strings, pointers, interfaces (as long as the values ​​contained in the interface are comparable), structures, arrays, etc. Slices, maps, and functions cannot be used as map keys because these types do not support equality comparison.

Error-prone points

In Go, arrays, slices, and maps are three commonly used data structures, each with different characteristics and considerations. Here are some points to note when using them:

Array

  • Avoid using very large arrays, as they will take up a lot of stack space and may cause a stack overflow.
  • Arrays are used when a fixed-size collection of data is required.

Slice

  • Memory reallocation may occur when a slice is expanded. You should try to pre-allocate sufficient capacity to avoid frequent expansion.
  • Avoid using very large slices, as they may take up a lot of heap space.
  • Note that the zero value of a slice isnil, needs to be initialized before use.

map

  • The zero value of the map isnilnilMap cannot be used to store key-value pairs and must be initialized before use.
  • In a concurrent environment, map read and write operations are not thread-safe and need to be protected by mutex locks or other synchronization mechanisms.
  • usedeleteDeleting a nonexistent key will not generate an error, but it is safe to check if the key exists.