This is a creation in Article, where the information may have evolved or changed.
- Original: http://golang.org/doc/articles/slices_usage_and_internals.html
- English: http://zh-golang.appsp0t.com/doc/articles/slices_usage_and_internals.html
The slice type of go provides a convenient and efficient way to work with the same type of data series. Slices are somewhat similar to arrays in other languages, but there are some unusual features. This article will delve into the nature of the slice and explain its usage.
Array
The slice of Go is an abstract data type on top of the array, so you have to understand the array before you know the slices.
The array type is defined by the specified and length and element types. For example, [4]int a type represents a sequence of four integers. The length of the array is fixed, and the length is part of the array type ( int[4] and [5]int is a completely different type). The array can be accessed as a regular index, and the expression s[n] accesses the first element of the array n .
var a [4]inta[0] = 1i := a[0]// i == 1
Arrays do not require explicit initialization, and array elements are automatically initialized to 0 values:
// a[2] == 0, the zero value of the int type
Type 4int corresponds to four consecutive integers in memory:
The array of Go is the value semantics. An array variable represents the entire array, which is not a pointer to the first element (such as an array of C languages). When an array variable is assigned or passed, the entire array is actually copied. (To avoid copying an array, you can pass a pointer to an array, but the array pointer is not an array.) You can treat an array as a special struct whose field names correspond to the index of the array, and the number of members is fixed.
The literal value of the array is like this:
b := [2]string{"Penn", "Teller"}
Of course, you can also have the compiler count the number of elements in the array literals:
b := [...]string{"Penn", "Teller"}
Both of these are the b corresponding [2]string types.
Slice
Arrays have a place for them, but arrays are not flexible enough, so arrays in go code are not used much. However, slices are used quite extensively. Slices are built on an array, but provide stronger functionality and convenience.
The type of slice is the type of the []T T slice element. Unlike arrays, slices do not have a fixed length.
Slices have a very similar literal value and array literals, but the slices do not have the specified number of elements:
letters := []string{"a", "b", "c", "d"}
Slices can be created with built-in functions make , and function signatures are:
func make([]T, len, cap) []T
TRepresents the type of slice element being created. makethe function accepts a type, a length, and an optional capacity parameter. makewhen called, an array is allocated internally, and the corresponding slice of the array is returned.
var s []bytes = make([]byte, 5, 5)// s == []byte{0, 0, 0, 0, 0}
When the capacity parameter is ignored, it defaults to the specified length. The following is a concise notation:
s := make([]byte, 5)
You can use built-in functions len and cap get information about the length and capacity of slices.
len(s) == 5cap(s) == 5
The next two sections discuss the relationship between length and capacity.
The 0 value of the slice type variable is nil . For 0-Valued tile variables, len and cap both will be returned 0 .
Slices can also be generated based on existing slices or arrays. The range of the slices is specified by the half-open intervals corresponding to the two indexes separated by colons. For example, an expression b[1:4] creates a slice that references the first b element space of an array 1 3 (the corresponding slice has an index of 0 to 2).
b := []byte{'g', 'o', 'l', 'a', 'n', 'g'}// b[1:4] == []byte{'o', 'l', 'a'}, sharing the same storage as b
The start and end indexes of a slice are optional, and they default to zero and the length of the array, respectively.
// b[:2] == []byte{'g', 'o'}// b[2:] == []byte{'l', 'a', 'n', 'g'}// b[:] == b
The following syntax also creates a slice based on an array:
x := [3]string{"Лайка", "Белка", "Стрелка"}s := x[:] // a slice referencing the storage of x
The nature of the slices
A slice is a description of an array cut interval. It contains a pointer to an array, the length of the cut interval, and the capacity (the maximum length of the cut interval).
The previously make([]byte, 5) created tile variable s structure is as follows:
The length is the number of elements referenced by the slice. The capacity is the number of elements in the underlying array (starting with the tile pointer). About length and capacity and area will be illustrated in the next example.
We continue to slice s , observe the data structure of the slice and the underlying array it references:
s = s[2:4]
Slices do not copy the entire slice element. It creates a new slice to execute the same underlying array. This makes the slice operation as efficient as the array index. Therefore, modifying an element with a new slice also affects the original slice.
d := []byte{'r', 'o', 'a', 'd'}e := d[2:] // e == []byte{'a', 'd'}e[1] = 'm'// e == []byte{'a', 'm'}// d == []byte{'r', 'o', 'a', 'm'}
The length of the tile you created earlier is s less than its capacity. We can grow the length of the slice for its capacity:
s = s[:cap(s)]
Slice growth cannot exceed its capacity. Growth exceeding the tile capacity will result in a run-time exception, just as the index of a slice or array is out of range. Similarly, you cannot use an index that is less than 0 to access the element before the slice.
Slice growth (copy and append)
To increase the capacity of a slice you must create a new, larger tile, and then copy the contents of the original slice to the new slice. The entire technique is a common implementation that supports dynamic array languages. The following example doubles the slice s capacity by creating a new twice-fold slice t , copying s The elements to t , and assigning the values to t s :
t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0for i := range s { t[i] = s[i]}s = t
Operations copied in a loop can be copy replaced by built-in functions. The copy function copies the elements of the source slice to the destination slice. It returns the number of copied elements.
func copy(dst, src []T) int
copyThe function supports copying between slices of different lengths (it duplicates only the elements of the minimum slice length). In addition, the copy function can correctly handle the case where the source and destination slices overlap.
Using copy functions, we can simplify the code snippet above:
t := make([]byte, len(s), (cap(s)+1)*2)copy(t, s)s = t
A common operation is to append data to the end of a slice. The following function appends the element to the end of the slice, increasing the capacity of the slice if necessary, and finally returning the updated slice:
func AppendByte(slice []byte, data ...byte) []byte { m := len(slice) n := m + len(data) if n > cap(slice) { // if necessary, reallocate // allocate double what's needed, for future growth. newSlice := make([]byte, (n+1)*2) copy(newSlice, slice) slice = newSlice } slice = slice[0:n] copy(slice[m:n], data) return slice}
Here's AppendByte a way to use:
p := []byte{2, 3, 5}p = AppendByte(p, 7, 11, 13)// p == []byte{2, 3, 5, 7, 11, 13}
A similar AppendByte function is more practical because it provides complete control over the growth of the tile capacity. Depending on the program's characteristics, you may want to allocate smaller, larger blocks, or more than a certain size redistribution.
But most programs do not require full control, so go provides a built-in function append for most occasions; its function signature:
The Append function appends x to the end of the slice s and increases the capacity when necessary.
a := make([]int, 1)// a == []int{0}a = append(a, 1, 2, 3)// a == []int{0, 1, 2, 3}
If you are appending one slice to the end of another slice, you need to use ... The syntax expands the 2nd parameter to a parameter list.
a := []string{"John", "Paul"}b := []string{"George", "Ringo", "Pete"}a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])"// a == []string{"John", "Paul", "George", "Ringo", "Pete"}
You can declare a 0-value slice ( nil ), and then append data to the slice in the loop:
// Filter returns a new slice holding only// the elements of s that satisfy f()func Filter(s []int, fn func(int) bool) []int { var p []int // == nil for _, i := range s { if fn(i) { p = append(p, i) } } return p}
Possible "traps"
As previously mentioned, the slice operation does not replicate the underlying array. An array of this layer will be stored in memory, knowing that it is no longer referenced. Sometimes it is possible for a small memory reference to cause all of the data to be saved.
For example, the FindDigits function loads the entire file into memory, then searches for the first consecutive number, and the final result is returned as a slice.
var digitRegexp = regexp.MustCompile("[0-9]+")func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) return digitRegexp.Find(b)}
This code behaves like an ad, and returns an array that points to the []byte entire file being saved. Because the slice references the original array, which causes the GC to not free the space of the array, a small requirement causes the entire file to be saved.
To fix the whole problem, you can copy the data you are interested in to a new slice:
func CopyDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) b = digitRegexp.Find(b) c := make([]byte, len(b)) copy(c, b) return c}
You can use append the implementation of a more concise version. This is left to the reader as an exercise.
Advanced Reading
Effective go has an in-depth discussion of slices and arrays, and the Go Language specification defines the related, auxiliary, and proprietary functions of slices.