slice

package main

import "fmt"

func main() {
    /*
    slice :拥有相同类型元素的可变长的序列。 通常写成 []T,T是元素类型。
    slice和数组的关联:slice是一种轻量级的数据结构,可以用来访问数组的部分或者全部元素
        ,而这个数组成为slice的底层数组。一个底层数组可以对应多个slice
    slice三个属性:指针、长度、容量
        1 指针:指向数组的第一个可以从slice中访问的元素,并不一定是数组的第一个元素
        2 长度:slice中元素的个数,不能超过容量
        3 容量:从slice的起始元素到底层数组的最后一个元素间元素的个数
    定义:s[i,j]  s可以使数组也可以是指向数组的指针
    */

    //定义一个底层数组
    months:=[...]string{
        1:"January", 2:"February",3:"March",
        4:"April", 5:"May",6:"June",
        7:"July",8:"August",9:"September",
        10:"October",11:"November",12:"December"}

    Q2:=months[4:7] //创建slice Q2,引用的是months数组的4,5,6
    summer:=months[6:9]
    fmt.Println(Q2)   //  [April May June]    两个slice Q2 summer中都有June元素
    fmt.Println(summer)   //  [June July August]

    //遍历 取 相同元素
    for _,s:=range summer{
        for _,q:=range Q2{
            if s ==q{
                /*%T 类型占位符 %v 值占位符 %d 整型占位符 %f 浮点型占位符
                %c 字符占位符 %s 字符串占位符 %p指针占位符*/
                fmt.Printf("%s appears in both\n", s)
            }
        }
    }
    scap:=cap(summer)
    fmt.Println(scap)   //7
    //如果slice的引用超过了被引用对象的容量,即 cap(s),则宕机;如果超过了备用用对象的长度,及len(s),那么最终slice回避原slicec长
    //fmt.Println(summer[:20])   //宕机 超过了被引用对象的容量
    endlessSummer:=summer[:5]
    fmt.Println(endlessSummer)  // [June July August September October] 超过了备用用对象的长度

    /* 注意
    求字符串(string)子串操作 和 对字节slice([]byte)做slice 操作 是相似的,都协作x[m:n],都返回原始字节的一个子序列。
    同时,他们的底层引用方式一样,所以两个操作都是消耗常量时间。
    区别在于:如果x是字符串, x[m,n]返回的是一个字符串;
            如果x是字节slice([]byte) ,x[m,n]返回的是 字节slice ([]byte)
    */

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

    reverse(a[:])    // a[:] 标识引用了正式数组
    fmt.Println(a)   //[5 4 3 2 1 0]
    fmt.Printf("a格式:%T",a)

    // 将一个slice左移n个元素: 连续调用resverse三次
    s:=[]int{0,1,2,3,4,5}
    //向左移动两个元素
    reverse(s[:2])
    reverse(s[2:])
    reverse(s)
    fmt.Println(s) //[2 3 4 5 0 1]
    q:=[]int{0,1,2,3,4,5}
    //向右移动两个元素
    reverse(q[4:])
    reverse(q[:4])
    reverse(q)
    fmt.Println(q)
    /* 注意 数组a  slice s的初始化表达式区别。a是创建有固定长度的数组,s是创建指向数组的slice.。
            slice也是按照顺序指定元素,也可以按照索引来指定元素,或两者结合
            区别1 数组可以比较,slice无法比较,因此无法使用==来测试slic是否拥有相同的元素
            标准库里给处理函数 bytes.Equal来比较两个字节slice([]byte).对于其他的
            slice,我们必须自己写函数比较
            区别2 slice的元素是间接的,如果底层数组的元素改变,sliced的元素原会改变
            slice唯一允许的比较操作就是和nil作比较:例如
            if summer == nil{......}
            slice 零值是 nil,值为nil的slice没有对应的底层数组,长度和容量都为零
    */
    // p:=[]int{1:1,0:0,3,4} 两者结合 报错.\slices.go:73:19: duplicate index in array literal: 1
    p:=[]int{1:1,0:0,3:3,4}  //两者结合 ok  [0 1 0 3 4]
    fmt.Println(p)
    fmt.Printf("%T",p)


    /*内置函数 make
    作用:创建一个具有制定元素类型、长度和容量的slice。容量可以省略,此时,slice的长度与容量相等
    */
    sm:=make([]int,8)
    println(sm)
}

func reverse(s []int)  {
    // 初始值i,j:=0,len(s)-1  条件 i<j 表达式 i,j=i+1,j-1
    for i,j:=0,len(s)-1;i<j;i,j=i+1,j-1{
        s[i],s[j]=s[j],s[i]
    }

}

 append()函数

package main

import "fmt"

func main() {
    //rune类型 http://c.biancheng.net/view/18.html
    var runes []rune
    /*append函数
    作用:将元素追加到slice后面
    通常将append的调用结果再次复制给传入applend函数的slice,更新slice变量
    对于任何函数,只要有可能改变slice的长度或者容量,或者使slice指向不同的底层数组
    都需要更新slice变量
    */
    for _,r:=range "good"{
        runes=append(runes,r)
    }
    fmt.Printf("%q\n",runes) //['g' 'o' 'o' 'd']


    var x,y []int
    for i:=0;i<10;i++{
        y=appendInt(x,i)
        fmt.Printf("%d cap=%d\t%v\n",i,cap(y),y)
        x=y
    }


}


//为[]int数组slice定义方法appendInt

func appendInt(x []int,y int) []int {
    var z []int
    zlen := len(x)+1
    //检查slice是否有足够容量来存储数组中的新元素
    if zlen <= cap(x){
        z=x[:zlen]  //有足够的容量则 定义新的slice(底层数组未变),将元素y复制到新位置
    }else{
        /*slice已无空间,为他分配新的底层数组,不够长则创建一个足够长的新的底层数组来存储新元素,
        增加两倍的目的是为了达到分摊线性复杂性
        */
        /*出于效率考录,新创建的数组容量会比实际容纳slice x slice y 所需的最小长度更大一i但。
        在每次数组容量扩容是,通过扩展已被的容量来减少内存分配的次数,也可以保证追加一个元素消耗的
        是固定时间
        每次slice容量的改变都意味着一次底层数组重新分配和元素复制
        */
        zcap:=zlen
        if zcap<2*len(x){
            zcap=2*len(x)
        }
        z=make([]int,zlen,zcap)   //定义新的slice
        /*内置copy函数:为两个相同类型元素的slice复制元素 copy(目标slice,源slice)
                copy()的返回值是实际复制的元素个数,是较小的slice的长度

        var a=[]int{1,2,3,5,8,7,10}
        var b=[]int{1,2,3,4,6,10}
        d:=copy(b,a)
        fmt.Println(d)   //6
        fmt.Println(b)   //[1 2 3 5 8 7]

        所以不存在复制导致的索引越界
        */
        copy(z,x)

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

}

 就地更改slice

package main

import "fmt"

func main()  {
    data:=[]string{"one","","three"}
    fmt.Printf("%q\n",nonemty(data)) //["one" "three"]
    fmt.Printf("%q\n",data) //["one" "three" "three"]


    s:=[]int{5,6,7,8,9}
    fmt.Println(remove(s,2))  // [5 6 8 9]
}

//输入和输出有相同的底层数组,避免在函数内部重新分配一个数组,底层数组的元素被部分修改
func nonemty(strings []string) []string {
    i:=0
    for _,s:=range strings{
        if s!=""{
            strings[i]=s
            i++
        }
    }
    return strings[:i]

}
//移除slice中的某个元素,保持原来的顺序不变
func remove(slice []int,i int) []int  {
    //copy 把第二个切片的元素赋值copy到第一个切片总
    copy(slice[i:],slice[i+1:])
    return slice[:len(slice)-1]

}

 

posted on 2020-09-07 19:51  小胖子方法  阅读(1219)  评论(0编辑  收藏  举报

导航