Array,slice,map and Set in the Go language

Source: Internet
Author: User
Tags array length key string

Go from: https://se77en.cc/Array (array) internal mechanism

In the Go language, an array is a FIXED-LENGTH data type that contains contiguous elements of the same type, which can be built-in types, such as numbers and strings, or struct types, and elements can be accessed through unique indexed values, starting with 0.

An array is a valuable data structure because its memory allocation is contiguous, and memory continuity means that it stays in the CPU cache for longer, so that the iterations and moving elements will be very fast.

Array declaration and initialization

Declare an array by specifying the data type and the number of elements (array length).

// 声明一个长度为5的整数数组var array [5]int

Once the array is declared, its data type and length are no longer changed. If you need more elements, you can only create a new array of the length you want, and then copy the elements of the original array Past.

When any variable in the Go language is declared, it is initialized by default to the 0 value corresponding to the respective type, and the array is certainly no exception. When an array is declared, each element contained within it is initialized to a value of 0.

One way to quickly create and initialize arrays is to use array literals. Array literals allow us to declare the number of elements we need and specify the data type:

// 声明一个长度为5的整数数组// 初始化每个元素array := [5]int{7, 77, 777, 7777, 77777}

If you write the length ... , the Go compiler will infer the length based on your element:

// 通过初始化值的个数来推导出数组容量array := [...]int{7, 77, 777, 7777, 77777}

If we know the length of the array you want, but want to initialize the specified location element, you can:

// 声明一个长度为5的整数数组// 为索引为1和2的位置指定元素初始化// 剩余元素为0值array := [5]int{1: 77, 2: 777}
Working with arrays

Use [] an operator to access an array element:

array := [5]int{7, 77, 777, 7777, 77777}// 改变索引为2的元素的值array[2] = 1

We can define an array of pointers:

array := [5]*int{0: new(int), 1: new(int)}// 为索引为0和1的元素赋值*array[0] = 7*array[1] = 77

In the Go language, an array is a value, so it can be used for assignment operations. An array can be assigned to an array of any of the same types:

var array1 [5]stringarray2 := [5]string{"Red", "Blue", "Green", "Yellow", "Pink"}array1 = array2

Note that the type of the array includes both the length of the array and the type of elements that can be stored, and the array types are identical to each other, such as the Following:

var array1 [4]stringarray2 := [5]string{"Red", "Blue", "Green", "Yellow", "Pink"}array1 = array2// 编译器会报错Compiler Error:cannot use array2 (type [5]string) as type [4]string in assignment

Copying a pointer array is actually a copy of the pointer value, not the value pointed to by the Pointer:

var array1 [3]*stringarray2 := [3]*string{new(string), new(string), new(string)}*array2[0] = "Red"*array2[1] = "Blue"*array2[2] = "Green"array1 = array2// 赋值完成后,两组指针数组指向同一字符串
Multidimensional arrays

Arrays are always one-dimensional, but can be combined into multidimensional. Multidimensional arrays are typically used for data that has a parent-child relationship or coordinate system Data:

// 声明一个二维数组var array [4][2]int// 使用数组字面值声明并初始化array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}// 指定外部数组索引位置初始化array := [4][2]int{1: {20, 21}, 3: {40, 41}}// 同时指定内外部数组索引位置初始化array := [4][2]int{1: {0: 20}, 3: {1: 41}}

The array elements are also accessed through the [] operator:

var array [2][2]intarray[0][0] = 0array[0][1] = 1array[1][0] = 2array[1][1] = 3

similarly, multidimensional arrays of the same type can be assigned to each other:

var array1 = [2][2]intvar array2 = [2][2]intarray[0][0] = 0array[0][1] = 1array[1][0] = 2array[1][1] = 3array1 = array2

Because the array is a value, we can copy a separate dimension:

var array3 [2]int = array1[1]var value int = array1[1][0]
Passing an array in a function

Passing an array in a function is a very expensive behavior because passing a variable between functions is always a pass value, so if the variable is an array, then it means passing the entire array, even if it is large and large ...

For a chestnut, create an array of millions of elements, and on a 64-bit machine it needs 8 megabytes of memory to see what happens when we declare it and pass it on:

var array [1e6]intfoo(array)func foo(array [1e6]int) {  ...}

Each time foo it is called, 8 megabytes of memory will be allocated on the STACK. Once the function returns, the stack is bounced and the memory is freed, requiring 8 megabytes of space each Time.

The Go language is certainly not so silly, there is a better way to pass an array in a function, which is to pass pointers to the array so that only 8 bytes of memory are allocated at a time:

var array [1e6]intfoo(&array)func foo(array *[1e6]int){  ...}

But note that if you change the value that the pointer points to in the function, the value of the original array will also be Changed. Fortunately slice (slicing) can help us deal with these problems, come together to See.

Slice (slicing) internal mechanisms and fundamentals

Slice is a dynamic array that can grow and shrink as we wish. Its growth operation is easy to use because there are built-in append methods. We can also simplify slice by relice Operation. Because Slice's underlying memory is continuously allocated, slice indexes, iterations, and garbage collection performance are Good.

Slice is the abstraction and control of the underlying array. It contains three kinds of metadata that Go needs to be managed for the underlying array, namely:

    1. Pointer to underlying array
    2. Length of elements in slice
    3. Capacity of the slice (maximum value to be increased)
Create and initialize

There are many ways to create slice in Go, and we look at it one by One.

The first method is to use the built-in function make . When we use make create, an option is to specify the length of the slice:

slice := make([]string, 5)

If only the length is specified, the capacity defaults to the Length. We can specify the length and capacity separately:

slice := make([]int, 3, 5)

When we specify the length and capacity separately, The slice we create can have the capacity of the underlying array that was initially not accessed. In the slice of the above code, 3 elements can be accessed, but the underlying array has 5 Elements. Two elements that are irrelevant in length can be slice. The newly created slice can also share the underlying array and the existing Capacity.

Creating slice with a length greater than capacity is not allowed:

slice := make([]int, 5, 3)Compiler Error:len larger than cap in make([]int)

The usual way to create slice is to use slice literals. is similar to creating an array, but does not specify [] the value IN. The initial length and capacity depend on the number of elements:

// 创建一个字符串 slice// 长度和容量都是 5slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}

When creating slice with slice literals, there is a way to initialize the length and capacity, which is to initialize the Index. Here's an example:

// 创建一个字符串 slice// 初始化一个有100个元素的空的字符串 sliceslice := []string{99: ""}
Nil and empty slice

Sometimes we need to create a nil slice, and the way to create a nil slice is to declare it but not initialize It:

var slice []int

Creating a nil Slice is the most basic way to create slice, which can be used by many standard libraries and built-in Functions. It becomes very useful when we want to represent a slice that does not exist, such as when an exception occurs in a function that returns SLICE.

The way to create the empty slice is to declare and initialize It:

// 使用 make 创建silce := make([]int, 0)// 使用 slice 字面值创建slice := []int{}

Empty slice contains 0 elements and the underlying array does not allocate storage space. It is useful when we want to represent an empty collection, such as a database query that returns 0 Results.

Whether we use nil slice or empty slice, the built-in append functions len cap work exactly the same way.

Using slice

Assigning a slice assignment to a specified index value is exactly the same as assigning a value to the previous Array. To change the value of a single element using the [] operator:

slice := []int{10, 20, 30, 40, 50}slice[1] = 25

We can slice a subset of the data on the underlying array to create a new slice:

// 长度为5,容量为5slice := []int{10, 20, 30, 40, 50}// 长度为2,容量为4newSlice := slice[1:3]

After the slice operation we got two slice, which share the underlying array. however, They can access the range of the underlying array, but Newslice cannot access the value in front of its head pointer.

The following formula can be used to calculate the length and capacity of any new slice:

对于 slice[i:j] 和底层容量为 k 的数组长度:j - i容量:k - i

It must be clarified again that it is now two slice share the underlying array, so as long as one slice changes the value of the underlying array, the other will change as Well:

slice := []int{10, 20, 30, 40, 50}newSlice := slice[1:3]newSlice[1] = 35

Changing the value of the second element of the Newslice also changes the value of the third element of the Slice.

An slice can only access indexes within its length range, and attempting to access an index beyond the length range results in a run-time error. the capacity can only be used to grow, and it can only be accessed if it is merged into length (this sentence is wrong, explained in blue font):

slice := []int{10, 20, 30, 40, 50}newSlice := slice[1:3]newSlice[3] = 45Runtime Exception:panic: runtime error: index out of range

The capacity can be incorporated into the length, through the built-in append functions. That is, the capacity cannot be changed, and the length can be increased by append

"go concurrent Programming Combat First edition" P55 has a good explanation:

"we can think of slices as a window toward the underlying array." This window is the way we look at the values of elements in the underlying array. The length of this value is the number of element values in the lower array that we can currently see. And his capacity represents the maximum number of elements in the current underlying array that we see "

Array: = [...]string{"Go","Python"."Java","C","C + +","PHP"}slice:= Array[:4]slice[4]//Panic is raised, and the index value exceeds the length of the Slice. Can see go to CSlice = Slice[:cap (slice)]//enlarged the length of the slice, and now the slice is as large as the host. you can see go to PHPSlice = slice[:4: Cap (slice)-1]//Slice most see go to C + +
Slice growth

The advantage of the slice array is that it can grow as we want, we just need to use append the method, and Go will do everything for us.

appendwhen using a method we need a source slice and a value that needs to be attached to it. When the append method returns, it returns a new slice, the append method always grows the length of the slice, and on the other hand, if the source slice has enough capacity, the underlying array will not change, otherwise the memory space will be redistributed.

// 创建一个长度和容量都为5的 sliceslice := []int{10, 20, 30, 40, 50}// 创建一个新的 slicenewSlice := slice[1:3]// 为新的 slice append 一个值newSlice = append(newSlice, 60)

Since Newslice has available capacity, the append value of slice index 3 after the operation has also become 60, which was previously said because Slice and newslice share the same underlying array.

If there is not enough capacity available, the append function creates a new underlying array, copies the existing values and the new values that will be appended:

// 创建长度和容量都为4的 sliceslice := []int{10, 20, 30, 40}// 附加一个新值到 slice,因为超出了容量,所以会创建新的底层数组newSlice := append(slice, 50)

appendWhen the function re-creates the underlying array, the capacity is twice times the size of the existing element (provided that the number of elements is less than 1000), and if the number of elements exceeds 1000, the capacity increases by 1.25 times Times.

The third index parameter of a slice

Slice can also have a third index parameter to limit capacity, which is not intended to increase capacity, but rather provides a protective mechanism for the underlying array to facilitate better control of the append operation, for example, a chestnut:

source := []string{"apple", "orange", "plum", "banana", "grape"}// 接着我们在源 slice 之上创建一个新的 sliceslice := source[2:3:4]

The newly created slice has a length of 1 and a capacity of 2, and it is easy to see the calculation formula for length and capacity:

对于 slice[i:j:k]  或者 [2:3:4]长度: j - i       或者   3 - 2容量: k - i       或者   4 - 2

If we try to set a larger capacity than the available capacity, we get a run-time error:

slice := source[2:3:6]Runtime Error:panic: runtime error: slice bounds out of range

The greatest use of capacity is to limit the capacity to the same length when creating new slice, so that when you add elements to the new slice later, the new underlying array will be allocated without affecting the original slice values:

source := []string{"apple", "orange", "plum", "banana", "grape"}// 接着我们在源 slice 之上创建一个新的 slice// 并且设置长度和容量相同slice := source[2:3:3]// 添加一个新元素slice = append(slice, "kiwi")

If there is no third index parameter qualification, adding Kiwi this element will overwrite the BANANA.

The built-in function append is a variable parameter function, meaning that you can add multiple elements at once, such as:

s1 := []int{1, 2}s2 := []int{3, 4}fmt.Printf("%v\n", append(s1, s2...))Output:[1 2 3 4]
Iterative Slice

Slice is also a collection, so it can be iterated, using for mates range to Iterate:

slice := []int{10, 20, 30, 40, 50}for index, value := range slice {  fmt.Printf("Index: %d  Value: %d\n", index, value)}Output:Index: 0  Value: 10Index: 1  Value: 20Index: 2  Value: 30Index: 3  Value: 40Index: 4  Value: 50

When iterating, the range keyword returns two values, the first one is the index value, and the second is the copy of the index position Value. Note : A copy of the value is returned instead of a reference, and if we use the address of the value as a pointer, we get an error to see Why:

slice := []int{10, 20, 30 ,40}for index, value := range slice {  fmt.Printf("Value: %d  Value-Addr: %X  ElemAddr: %X\n", value, &value, &slice[index])}Output:Value: 10  Value-Addr: 10500168  ElemAddr: 1052E100Value: 20  Value-Addr: 10500168  ElemAddr: 1052E104Value: 30  Value-Addr: 10500168  ElemAddr: 1052E108Value: 40  Value-Addr: 10500168  ElemAddr: 1052E10C

The address of the value variable is always the same because it contains only one copy. If you want to get the true address of each element you can use &slice[index].

If you do not need an index value, you can use the _ operator to ignore It:

slice := []int{10, 20, 30, 40}for _, value := range slice {  fmt.Printf("Value: %d\n", value)}Output:Value: 10Value: 20Value: 30Value: 40

rangeAlways iterate from the beginning once, if you want to control the traversal of the step, use the traditional for loop:

slice := []int{10, 20, 30, 40}for index := 2; index < len(slice); index++ {  fmt.Printf("Index: %d  Value: %d\n", index, slice[index])}Output:Index: 2  Value: 30Index: 3  Value: 40

As with arrays, the other two built-in functions len and cap returns the length and capacity of the slice, respectively.

Multidimensional Slice

As with arrays, Slice can be combined into multidimensional slice:

slice := [][]int{{10}, {20, 30}}

It is important to note the append behavior when using the method, such as we are now adding an element to Slice[0]:

slice := [][]int{{10}, {20, 30}}slice[0] = append(slice[0], 20)

Then only slice[0] will recreate the underlying array, slice[1] will Not.

Passing a slice between functions

Passing slice between functions is cheap because slice is a pointer to the underlying array, let's create a very large slice and then pass it to the function to call It:

slice := make([]int, 1e6)slice = foo(slice)func foo(slice []int) []int {    ...    return slice}

On a 64-bit machine, the slice requires 24 bytes of memory, where the pointer portion requires 8 bytes, and the length and capacity require 8 bytes Respectively.

Map internal mechanism

Map is a collection of unordered Key-value Pairs. The most important point of map is to quickly retrieve the data by key, which is similar to the index, which points to the value of the Data.

Map is a collection, so we can iterate over it like an iterative algebraic group and Slice. however, Map is unordered, and we cannot determine the order in which it is returned because the map is implemented using a hash table.

The hash table for map contains a collection of buckets (collection of buckets). When we store, remove, or look up a key-value pair (key/value pair), We start by selecting a Bucket. During the map operation, we pass the specified key value (key) to the hash function (also known as the hash function). The function of the hash function is to generate an index, and the index is evenly distributed across all available Buckets. Hash table algorithm see: July's Blog – Thoroughly parse hash table algorithm from beginning to end

Create and initialize

There are several ways to create and initialize a map in the Go language. We can use the built-in functions as make well as map literals:

// 通过 make 来创建dict := make(map[string]int)// 通过字面值创建dict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}

Using literals is a way to create a map idiom (why not use make). The length of the initialization map depends on the number of Key-value Pairs.

The Map's key can be any built-in or struct-type, and the Map's value can be an == expression using the Operator. Slice,function and the struct type containing the slice cannot be the key to the map, or it will compile an error:

dict := map[[]string]int{}Compiler Exception:invalid map key type []string
Using map

Assigning a value to a map is specifying a valid type of key and assigning the value to the Key:

colors := map[string]string{}colors["Red"] = "#da1337"

If you do not initialize the map, a nil map is Created. Nil map cannot be used to store key-value pairs, otherwise it will report a run-time error:

var colors map[string]stringcolors["Red"] = "#da1337"Runtime Error:panic: runtime error: assignment to entry in nil map

Testing the presence of a map key is an important part of the map operation because it allows us to determine whether an operation can be performed or to cache a value in the MAP. It can also be used to compare whether the key-value pairs of two maps match or are missing.

There are two options for retrieving a value from a map, and we can retrieve the value at the same time and determine whether the key exists:

value, exists := colors["Blue"]if exists {  fmt.Println(value)}

Another option is to return only the value, and then determine if it is a value of 0 to determine whether the key Exists. But it only works when you're sure that the value of 0 is Illegal:

value := colors["Blue"]if value != "" {  fmt.Println(value)}

When a map is indexed, it always returns a value even if the key does not exist. The above example returns a value of 0 for the corresponding type.

Iterating over a map and an array of iterations is the same as slice, using the range keyword, but we don't use the key/value structure when we iterate the map using index/value:

colors := map[string]string{    "AliceBlue":   "#f0f8ff",    "Coral":       "#ff7F50",    "DarkGray":    "#a9a9a9",    "ForestGreen": "#228b22",}for key, value := range colors {  fmt.Printf("Key: %s  Value: %s\n", key, value)}

If we want to remove a key-value pair from the map, use the built delete -in function (if You can also return to the success of the removal, eh ...) ):

delete(colors, "Coral")for key, value := range colors {  fmt.Println("Key: %s  Value: %s\n", key, value)}
Passing a map between functions

Passing a map between functions is not a copy of the pass Map. So if we change the map in the function, then all the places that reference the map will Change:

func main() {  colors := map[string]string{     "AliceBlue":   "#f0f8ff",     "Coral":       "#ff7F50",     "DarkGray":    "#a9a9a9",     "ForestGreen": "#228b22",  }  for key, value := range colors {      fmt.Printf("Key: %s  Value: %s\n", key, value)  }  removeColor(colors, "Coral")  for key, value := range colors {      fmt.Printf("Key: %s  Value: %s\n", key, value)  }}func removeColor(colors map[string]string, key string) {    delete(colors, key)}

Execution gets the following results:

Key: AliceBlue Value: #F0F8FFKey: Coral Value: #FF7F50Key: DarkGray Value: #A9A9A9Key: ForestGreen Value: #228B22    Key: AliceBlue Value: #F0F8FFKey: DarkGray Value: #A9A9A9Key: ForestGreen Value: #228B22

It can be seen that passing a map is also very inexpensive, similar to Slice.

Set

The Go language itself does not provide set, but we can implement it ourselves, let's try it here:

Package Mainimport ("fmt" "sync") type Set struct {m map[int]bool sync. Rwmutex}func New () *set {return &set{m:map[int]bool{},}}func (s *set) Add (item int) {s.lock () defer S.unlo CK () s.m[item] = true}func (s *set) Remove (item int) {s.lock () s.unlock () Delete (s.m, item)}func (s *set) have (item in t) bool {s.rlock () defer s.runlock () _, ok: = s.m[item] return ok}func (s *set) len () int {return Len (s.list ())}fun    C (s *set) Clear () {s.lock defer s.unlock () s.m = map[int]bool{}}func (s *set) IsEmpty () bool {if s.len () = = 0 { Return true} return false}func (s *set) list () []int {s.rlock () defer s.runlock () list: = []int{} for item: = Rang E s.m {list = append (list, item)} return list}func main () {//initialize s: = New () s.add (1) s.add (1) s.add (2) S. Clear () if S.isempty () {fmt. Println ("0 item")} s.add (1) s.add (2) s.add (3) if S.has (2) {fmt. Println ("2 does exist")} s.remove (2) s.remove (3) Fmt. Println ("list of all itEMS ", S.list ())} 

Note that we just use int as the key, you can use interface{} as the key to make a more general Set, in addition, the implementation is Thread-safe.

Summarize
    • Arrays are the underlying structure of slice and map.
    • Slice is a method of collection data used in Go, and map is a way to store Key-value Pairs.
    • The built-in functions make are used to create slice and maps, specifying their length and capacity, and so On. Slice and map literals can do the same thing.
    • Slice have a capacity constraint, but you can add elements by using built-in functions append .
    • Map has no capacity one to say, so there is no growth limit.
    • The built-in functions len can be used to obtain the lengths of slice and maps.
    • The built-in function cap only works on Slice.
    • You can create multidimensional arrays and slice by combining them. The value of map can be slice or another map. Slice cannot be a key to a map.
    • Passing slice and maps between functions is fairly inexpensive because they do not pass copies of the underlying ARRAY.

Array,slice,map and Set in the Go language

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.