Go-slice

拥有相同类型元素的可变长度序列。通常写作[]T, 元素类型都T, 可以访问数组的部分或全部元素,这个数组称为slice底层数组。

属性

slice有三个属性:指针、长度和容量。

指针指向数组的第一个可以从slice中访问的元素。

长度是slice中元素的个数,不能超过slice的容量。

容量是从slice的起始元素到底层数组的最后一个元素间的元素个数。

slice操作符 s[i:j] (其中i<=j<=cap(s)) 创建了一个新的slilce,引用了序列S中从i到j-1索引位置的所有元素。

S可以是数组、指向数组的指针、或slice.

如果表达式省略了i,则i = 0; 如果省略了j,则j = len(s)

因为slice包含了指向数组的指针,所以将一个slice传递给函数,可以在函数内部修改底层数组的元素。

//就地反转一个整型slice中的元素
func reverse (s []int){
    for i,j := 0,len(s) - 1; i, j = i +1, j -1 {
        s[i], s[j] = s[j], s[i]
    }
}

a := [...]int{0,1,2,3,4,5}
reverse( a[:] )

初始化slice

用逗号分隔并用花括号括起来的元素序列,但slice并没有指定长度。

如何初始化slice:

//方法1:用make
var s1 []string = make([]string, 0) 
var s2 []string = make([]string, 1) 
var s3 []string = make([]string, 20)

方法2:静态显式初始化
var s1 []string = []string {}
var s2 []string = []string{"aa"} 
var s3 []string = []string{"aa", "bb", "cc"} 

var s4 = []string{} 
var s5 = []string{"aa"} 
var s6 = []string{"aa", "bb", "cc"} 

s7 := []string{} 
s8 := []string{"aa"} 
s9 := []string{"aa", "bb", "cc"}

这种区别是,创建固有长度的数组 和 指向数组的slice.

slice无法作比较,因为不能用 == 来测试两个slice是否拥有相同的元素,标准库里提供了bytes.Equal来比较两个字节slice([]byte).

string的slice比较:

func equal (x,y []string) bool{

    if len(x) != len(y){
        return false
    }

    for i := 0; i < len(x); i++{
        if x[i] !=y[i]
        return false
    }
    return true
}

为什么不可以直接用 == 比较 ?

1.slice元素是非直接的,有可能包含它自身;

2.如果底层数组元素改变,同一个slice在不同时间会有不同的元素。由于散列表仅对元素的键做浅拷贝,这要求散列表里键在散列表的整个生命周期内必须保持不变因为slice需要做深度比较,不能用slice做map的键

nil值比较

slice唯一允许的比较操作符是和nil做比较,slice类型的零值是nil,表示没有对应的底层数组;

一个nil值的slice的长度和容量是0;

也有非nil值的slice的长度和容量是0,如,[]int{} 或 make ([]int,3)[3:] ;

对任何类型,如果它们的值可以为nil,那这个类型的nil值,可使用转换表达式,如:[]int(nil)

var s []int     //len(s) == 0, s == nil
s = nil         //len(s) == 0, s== nil
s = []int(nil)  //len(s) == 0, s == nil
s = []int{}     //len(s) == 0, s != nil

检查一个slice是否为空,使用 len(s) == 0, 而不是 s == nil

内置函数make可以创建一个指定元素类型、长度和容量的slice,容量参数可以省略,此时长度和容量相等。

make ([]T,len)

make([]T, len, cap)

make创建了一个无名数组并返回了它的一个slice,这个数组仅可以通过这个slice访问。

//为rune类型添加元素,追加到slice后面:

var runes []rune
for _,r := range "Hello,世界"{
    runes = append(runes, r)
}

fmt.Printf("%q\n", runes)

//最方便的用法是 []rune("Hello,世界")

分析:slice添加元素

func appendInt (x []int, y int) []int{

    var z []int
    zlen := len(x) + 1

    if zlen < cap(x){
        //slice仍有增长空间,扩展slice内容
        z = x[:len]
    } else{
        //slice已无空间容量, 分配一个新的底层数组
        zcap := zlen
        if zcap < 2 * len(x){
            zcap = 2 * len(x)
        }

        z = make([]int, zlen, zcap)
        copy(z, x) //内置copy函数
    }

    z[len(x)] = y
    return z
}

容量足够:定义一个新的slice(仍引用原底层数组),将新元素复制到新的位置,并返回新slice.

【指向原底层数组的新slice】

容量不够:必须创建一个拥有足够容量的新的底层数组,然后将元素从原slice复制到这个数组,再将y追加到数组后面。

【指向新底层数组的新lsice】

copy函数

第一个参数是目标slice,第二个参数是原slice.

返回值:返回实际上复制的元素个数,这个值是两个slice长度较小的值。

遍历

slice := []int{0, 1, 2, 3}
for k,v :=range slice{
    fmt.Println("k:",k,"v:",v)
}

for range遍历的内容是对原内容的一个拷贝,所以不能用来修改原切片中内容;

如果要修改原slice的值,需要使用k根据索引位置直接修改:

for k,v :=range slice{
    if v==1 {
        slice[k]=100
    }
}

发布了127 篇原创文章 · 获赞 24 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/Linzhongyilisha/article/details/99687389