Golang understand - and an array of slices

Array

Array definition and characteristics in Go

An array is a sequence of particular type elements composed of a fixed length, it may be composed of an array of zero or more elements.

Because 数组的长度是固定的, therefore rarely used directly in an array Go language.

Type and array corresponds Slice (slice), it can grow and shrink dynamically sequence, slice function more flexible, it is understood that slice works, then we need to understand the array.

By default, each element of the array are initialized to zero value corresponding to the type of element, it is 0 for numeric type. We can also use an array literal syntax using a set of values ​​to initialize the array:

var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}
fmt.Println(r[2]) // "0"

Literals in the array, if there is in the longitudinal position of the array is "..." ellipsis, it indicates the length of the array is calculated based on the number of initialization values. Thus, the definition of q above the array can be simplified to

q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"

数组的长度是数组类型的一个组成部分Therefore [3] int and [4] int are two different types of arrays. Length of the array must be a constant expression, because the length of the array is determined at compile time.

We will find an array, slice, map and structure literal wording is very similar. The above initialization sequence directly form a sequence of values, but can also specify a list of values ​​and the corresponding index mode initialization, as follows:

type Currency int

const (
    USD Currency = iota // 美元
    EUR                 // 欧元
    GBP                 // 英镑
    RMB                 // 人民币
)

symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}

fmt.Println(RMB, symbol[RMB]) // "3 ¥"

如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的This time we can 直接通过==比较运算符compare the two arrays 只有当两个数组的所有元素都是相等的时候数组才是相等的. Not equal comparison operator! = Follow the same rules.

How to pass an array function parameters

When you call a function when the function parameters of each call will be assigned to the internal function parameter variables, 函数参数变量接收的是一个复制的副本not variables of the original call.

Because 函数参数传递的机制导致传递大的数组类型将是低效的, and any changes to the array parameter are occurring on an array copied 并不能直接修改调用时原始的数组变量.

In this regard, Go language and the way to treat an array of many other programming languages, other programming languages ​​may be implicitly array as a reference or pointer to the object passed to the function is called.

Precautions

While the parameters to pass a pointer array is highly efficient, and also allows to modify the value of the array within the function,

However, the array is still rigid type, because the type of the array contains a rigid length information.

And an array there is no way to add or delete array elements. For these reasons, with certain exceptions need to address the specific size of the array, the array is still rarely used as a function parameter; instead, we generally use the slice instead of an array.


Slice

slice definition and characteristics of the go

Slice (Slice) represent variable-length sequences, each sequence element has the same type. Usually writing a slice type [] T, where T represents the type of slice elements; and array slice syntax like, but it is not a fixed length.

A data slice is a lightweight structure, provided access to the array sequence (or all) of the functional elements, and the bottom slice does reference to an array object.

A slice consists of three parts: the pointer, length and capacity.

  • Pointer to the first element of the underlying array element corresponding to the address of the slice, to be noted that the first element of the first slice is not necessarily an element of the array
  • The number of elements corresponding to the length of the slice
  • Length can not exceed the capacity, the capacity is usually from an end position to the start position of the slice of the underlying data. Built-in functions return len and cap length and volume slice.

Built-in functions return len and cap length and volume slice.

The underlying data can be shared among a plurality of slice, and the reference range may overlap portions of the array.

img

If the slicing operation exceeds the upper limit cap (s) will result in a panic exception, but beyond len (s) it is meant extended slice, since the length of the new slice becomes larger

Because slice值包含指向第一个slice元素的指针, therefore 向函数传递slice将允许在函数内部修改底层数组的元素.

in other words,复制一个slice只是对底层的数组创建了一个新的slice别名

Not compare (and arrays which differ) between the slice, so we can not use the "==" operator to determine whether it contains two slice elements all equal.

However, the standard library provides highly optimized bytes.Equal function to determine whether two byte slice is equal to ([] byte), but for other types of slice, we must begin to compare themselves to each element:

func equal(x, y []string) bool {
    if len(x) != len(y) {
        return false
    }
    for i := range x {
        // 逐个元素比较
        if x[i] != y[i] {
            return false
        }
    }
    return true
}

Why slice does not support the comparison?

  1. A slice of the element is indirectly referenced, a slice may even contain themselves. Although there are many ways to handle this situation, but no one is simple and effective.
  2. Because the elements of the slice is the indirect reference, a fixed slice value (Annotation: refers to the value slice itself, not the element) may contain different elements at different times, because the elements underlying array may be modified in Go. in the map only a simple shallow copy key, key holder which requires invariance (Annotation: e.g. slice expansion, will cause the value of its own / address changes) throughout the life cycle.
  3. For reference, like a pointer type or the like chan == equality test is based on whether the two reference the same object

We the above reasons 安全的做法是直接禁止slice之间的比较操作, slice the only legitimate comparison operation and is relatively nil.

Although slice can be compared and nil, but only some of which also need to pay attention to the details:

  1. a slice is equal to a value of zero nil. A slice nil value and not the underlying array.
  2. a nil value of the slice length and capacity are all 0, but there slice length and capacity of the non-nil value is 0, for example, [] int {} or make ([] int, 3) [3:]
  3. If you need to test whether a slice is empty, use len (s) == 0 to determine, should not be judged by s == nil. In addition to comparing the outer and equal nil, slice a nil value of the behavior and any other slice of the same length of 0;

Built-in functions to create a slice make element type, and capacity specified length. Capacity portion may be omitted, in which case, the capacity will be equal to the length.

make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]

At the bottom, make creating an anonymous array variable, and then returns a slice; only a reference to the underlying array variable anonymously by returning to a slice. In the first statement, slice is a view of the entire array. In the second statement, slice only refers to the first len ​​elements underlying array, but the capacity will contain a whole array. Additional elements are reserved for future growth with.

slice the principle of expansion

Let's look at how to slice the source code is written in to analyze the principle of expansion:

func growslice(et *_type, old slice, cap int) slice {
    if et.size == 0 {
        if cap < old.cap {
            panic(errorString("growslice: cap out of range"))
        }
        // append should not create a slice with nil pointer but non-zero len.
        // We assume that append doesn't need to preserve old.array in this case.
        return slice{unsafe.Pointer(&zerobase), old.len, cap}
    }

    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        // 小于1024,*2扩容
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // 大于1024,*1.25
            for newcap < cap {
                newcap += newcap / 4
            }
        }
    }
  // 下面代码省略
  ....
}

As seen from the above code, slice expansion rules are:

  1. Less than 1024, cap after each expansion = oldCap * 2
  2. Greater than 1024, each expansion cap = oldCap * 1.25

We can see that every expansion involves copy of the array, and then generate a new array (slice point to the new array), will bring additional costs such system, usually when we create a slice, it is recommended to use make function , given a business scenario more appropriate cap size, to avoid the expansion of the underlying array slice as copy to occur.

slice of memory tips

Case number one:

Input and output slice share a bottom slice array, which avoid allocating another array, but the original data may be overwritten: e.g.

func nonempty(strings []string) []string {
    i := 0
    for _, s := range strings {
        if s != "" {
            strings[i] = s
            i++
        }
    }
    return strings[:i]
}

// 输出:
data := []string{"one", "", "three"}
fmt.Printf("%q\n", nonempty(data)) // `["one" "three"]`
fmt.Printf("%q\n", data)           // `["one" "three" "three"]`

Similarly, using the append can achieve the same function:

func nonempty2(strings []string) []string {
    out := strings[:0] // zero-length slice of original
    for _, s := range strings {
        if s != "" {
            out = append(out, s)
        }
    }
    return out
}

Anyway implemented in such a way to reuse a slice generally requires up to each input value produces an output value, in fact many of these algorithms are used or combined filter element adjacent sequences.

This slice usage is more complex skills, although some techniques to slice, but for some applications is relatively clear and effective.

Case II:

Slice stack used to simulate the operation, namely in a slice stack append elements, shrinkage slice through the stack, pop stack elements:

// 入栈, push
stack = append(stack, v)

// 出栈, pop
stack = stack[:len(stack)-1]

Case 3:

To delete an element in the middle of the slice and save the original order of elements can be built-in copy function sub-slice behind a move forward in order to complete:

list := []int{1,2,3,4,5,6}
// 删除元素i,并保留原来的顺序,原理就是将i后面的元素按次序copy
copy(list[i:],list[i+1:])

To delete an element without maintaining the original order, we can simply cover element is removed with the last element:

func remove(slice []int, i int) []int {
    // 使用最后一个元素覆盖要删除的元素
    slice[i] = slice[len(slice)-1]
    // 返回新的slice
    return slice[:len(slice)-1]
}

Guess you like

Origin www.cnblogs.com/vinsent/p/11326417.html