zoukankan      html  css  js  c++  java
  • go——切片

    切片(slice)可以看作一种对数组的包装形式,它包装的数组为该切片的底层数组。
    反过来讲,切片是针对其底层数组中某个连续片段的描述,下面的代码声明了一个切片类型的变量:
    var ips = []string{"192.168.1.1","192.168.1.2","192.168.1.3"}
    与数组不同,切片的类型自变量(如[]string)并不携带长度信息。
    切片的长度是可变的,且并不是类型的一部分;只要元素类型相同,两个切片的类型就是相同的。
    此外,一个切片类型的零值总是nil,此零值的长度和容量都为0。

    切片本身并非动态数组或数组指针。它内部通过指针引用底层数组,设定相关属性将数据读写操作限定在指定区域内。
    其内部结构包含了3个元素:指向底层数组中某个元素的指针、切片的长度以及切片的容量。

    type slice struct {
    	array  unsafe.Pointer
    	len    int
    	cap    int
    }
    

    可基于数组或数组指针创建切片,以开始和结束索引位置确定所引用的数组片段。
    不支持反向索引,实际范围是一个右半开区间。

    package main
    
    import "fmt"
    
    func main() {
    	x := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    	fmt.Println(x[:], len(x[:]), cap(x[:]))
    	fmt.Println(x[2:5], len(x[2:5]), cap(x[2:5]))
    	fmt.Println(x[2:5:7], len(x[2:5:7]), cap(x[2:5:7]))
    	fmt.Println(x[4:], len(x[4:]), cap(x[4:]))
    	fmt.Println(x[:4], len(x[:4]), cap(x[:4]))
    	fmt.Println(x[:4:6], len(x[:4:6]), cap(x[:4:6]))
    }
    
    /*
                                       len  cap
    x[:]      [0 1 2 3 4 5 6 7 8 9]    10    10
    x[2:5]    [2 3 4]                  3     8
    x[2:5:7]  [2 3 4]                  3     5
    x[4:]     [4 5 6 7 8 9]            6     6
    x[:4]     [0 1 2 3]                4     10
    x[:4:6]   [0 1 2 3]                4     6
    */
    //[x:y:z]  x:切片的起始位置  y:切片的终止位置  z:容量的终止位置
    //cap表示切片所引用数组片段的真实长度
    //len用于限定可读的写元素数量
    

    和数组一样,切片同样使用索引号访问元素内容。
    起始索引为0,而非对应的底层数组和真实索引位置。

    package main
    
    import "fmt"
    
    func main() {
    	x := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    	s := x[2:5]
    
    	for i := 0; i < len(s); i++ {
    		fmt.Println(s[i])
    	}
    }
    
    /*
    2
    3
    4
    */
    

    可直接创建切片对象,无需预先准备数组。
    因为是引用类型,须使用make函数或显式初始化语句,它会自动完成底层数组内存分配。

    package main
    
    import "fmt"
    
    func main() {
    	s1 := make([]int, 3, 5) //make(初始化类型,长度,容量)  如果没有指定容量则与长度一致
    	s2 := make([]int, 3)
    	s3 := []int{10, 20, 5: 30} //第6个元素为30
    
    	fmt.Println(s1, len(s1), cap(s1)) //[0 0 0] 3 5
    	fmt.Println(s2, len(s2), cap(s2)) //[0 0 0] 3 3
    	fmt.Println(s3, len(s3), cap(s3)) //[10 20 0 0 0 30] 6 6
    }
    

    注意下面两种定义方式。

    package main
    
    import "fmt"
    
    func main() {
    	var a []int  //仅定义了一个[]int变量,并未执行初始化操作
    	b := []int{} //初始化表达式完成了全部创建过程
    
    	fmt.Println(a == nil, b == nil) //true false
    }
    

    不支持比较操作,就算元素类型支持也不行,仅能判断是否为nil。
    可获取元素地址,但不能像数组那样直接用指针访问元素内容。

    如果元素类型也是切片,那么就可实现类似交错数组功能。

    package main
    
    import "fmt"
    
    func main() {
    	x := [][]int{
    		{1, 2},
    		{10, 20, 30},
    		{100},
    	}
    	fmt.Println(x[1]) //取值
    
    	x[2] = append(x[2], 200, 300) //添加元素
    	fmt.Println(x)
    }
    
    /*
    [10 20 30]
    [[1 2] [10 20 30] [100 200 300]]
    */
    

    新建切片对象依旧指向原底层数组,也就是说修改对所有关联切片可见。
    需要注意的是,当向新建切片中添加值的时候,并不会向底层数组中添加

    package main
    
    import "fmt"
    
    func main() {
    	d := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    	s1 := d[3:7]  //[3, 4, 5, 6]
    	s2 := s1[1:3] //[4, 5]
    
    	for i := range s2 {
    		s2[i] += 100 //[104, 105]
    	}
    	fmt.Println(d)
    	fmt.Println(s1)
    	fmt.Println(s2)
    }
    
    /*
    [0 1 2 3 104 105 6 7 8 9]
    [3 104 105 6]
    [104 105]
    */
    

    append向切片尾部添加数据,返回新的切片对象。

    package main
    
    import "fmt"
    
    func main() {
    	s := make([]int, 0, 5)
    
    	s1 := append(s, 10)
    	s2 := append(s1, 20, 30)
    
    	fmt.Println(s, len(s), cap(s))
    	fmt.Println(s1, len(s1), cap(s1))
    	fmt.Println(s2, len(s2), cap(s2))
    }
    
    /*
    [] 0 5
    [10] 1 5
    [10 20 30] 3 5
    */
    

    数组被追加到原底层数组。如果超出cap限制,则为新切片对象重新分配数组。

    package main
    
    import "fmt"
    
    func main() {
    	s := make([]int, 0, 100)
    	s1 := s[:2:4]
    	s2 := append(s1, 1, 2, 3, 4, 5, 6) //超出是s1 cap限制,分配新底层数组
    
    	fmt.Println("s1:", &s1[0], s1)
    	fmt.Println("s2:", &s2[0], s2)
    	fmt.Println("s[:10]:", s[:10])
    	fmt.Printf("s1 cap: %d, s2 cap: %d
    ", cap(s1), cap(s2))
    }
    
    /*
    s1: 0xc000088000 [0 0]
    s2: 0xc00007e040 [0 0 1 2 3 4 5 6]  //数组地址不同,确认新分配
    s[:10]: [0 0 0 0 0 0 0 0 0 0]       //append并未向原数组中写入数据
    s1 cap: 4, s2 cap: 8                //新数组是原cap的2倍,一般是2倍,但也不绝对
    */
    

      在两个切片数据间复制数据,允许指向同一底层数组,允许目标区间重叠。
    最终所复制长度以较短的切片长度(len)为准。

    package main
    
    import "fmt"
    
    func main() {
    	s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    
    	s1 := s[5:8]
    	n := copy(s[4:], s1)
    	fmt.Println(n, s)
    
    	s2 := make([]int, 6)
    	n = copy(s2, s)
    	fmt.Println(n, s2)
    }
    
    /*
    3 [0 1 2 3 5 6 7 7 8 9]
    6 [0 1 2 3 5 6]
    */
    
  • 相关阅读:
    在linux上开发210的hdmi-servers输出
    haproxy.cfg
    【Quartz】Quartz的搭建、应用(单独使用Quartz)
    application.xml定时
    URLEncode转json
    Callable与Future的简单介绍
    Java并发:Callable、Future和FutureTask
    Java并发编程:Callable、Future和FutureTask
    EXECUTORSERVICE线程池讲解
    ExecutorService中submit和execute的区别
  • 原文地址:https://www.cnblogs.com/yangmingxianshen/p/10089983.html
Copyright © 2011-2022 走看看