Go语言--容器:存储和组织数据的方式--数组、切片

3.1 数组--固定大小的连续空间

3.1.1 声明数组

写法 var 数组变量名 [元素数量]T 说明: 变量名就是使用时的变量 元素的数量可以是表达式,最后必须为整型数值 T 可是是任意基本类型,包括数组本身,即可为多维数组

package main
import (
    "fmt"   
)
func main() {
    var team [5]string
    fmt.Println(team)
    // [    ]
    // 获取数组长度 -- len()
    fmt.Println(len(team))
    // 5
    // 添加元素
    team[1] = "one"
    team[2] = "two"
    fmt.Println(team)
    // [ one two  ]
}

3.1.2 初始化数组

package main
import ("fmt")
func main() {
    var team1 = [3]string{"one", "two", "three"}
    fmt.Println(team1)
    // [one two three]
    var team2 = [...]string{"one1", "two2", "three3"}
    fmt.Println(team2)
    // [one1 two2 three3]
    // ... 表示让编译器确定数组大小
}

3.1.3 变量数组 -- 访问每一个数组元素

两种方式遍历都可以

package main
import ("fmt")
func main() {
    for i := 0; i < len(team2); i++ {
        fmt.Println(team2[i])
    }
    // one1
    // two2
    // three3
    for _, v := range team1 {
        fmt.Println(v)
    }
    // one
    // two
    // three
    for _, v := range team2 {
        fmt.Println(v)
    }
    // one
    // two
    // three
}

3.2 切片(slice) -- 动态分配大小的连续空间

go语言切片的内部结构包含地址、大小和容量,切片是指快速地操作一块数据集合 比如数据集合作为切糕,切片就是准备切出来的那一块,切片地址就是切切糕的过程开始的位置,切多大的切糕就是切片大小,容量就是准备装切糕的袋子大小

3.2.1 从数组或切片生成新的切片

切片默认指向一段连续内存区域,可以数组,或切片本身 格式 slice [开始位置:结束位置] slice 表示目标切片对象 开始位置对应目标切片对象的索引 结束位置对应目标切片对象的结束索引

7种特性:

package main
import ("fmt")
func main() {
    var a = [3]int{1,2,3}
    fmt.Println(a)
    // [1 2 3]  
    // 特性:
    // 1.取数元素数量:结束位置-开始位置
    fmt.Println( a[0:2] )
    //[1 2]
    fmt.Println( len(a)-1 - 0)
    //2
    
    // 2.取出元素不包括结束位置对应的索引,切片最后一个元素使用slice[len(slice)]获取
    fmt.Println(a[0:1])
    //[1]
    
    // 3.当缺省开始位置时,表示从开头到结束位置
    fmt.Println(a[:2])
    // [1 2]
    
    // 4.单缺省结束位置时,表示从开始位置到末尾
    fmt.Println(a[0:])
    // [1 2 3]
    
    // 5.当两者都缺省时,与切片本身等效
    fmt.Println(a[:])
    // [1 2 3]
    
    // 6.两者为0时,等效于空切片,一般为切片复位
    fmt.Println(a[0:0])
    // []
    
    // 7.根据索引位置切片slice元素值时,取值范围是(0 ~ len(slice)-1 ),超界会报运行错误,生成切片时,结束位置可以填写len(slice)但不会报错
    fmt.Println(a[0:len(a)-1])
    // [1 2]
    
    // fmt.Println(a[0:4])
    // .slice.go:55:15: invalid slice index 4 (out of bounds for 3-element array)
    // exit status 2
    // Process exiting with code: 1
}

1从指定范围中生成切片

  var nums [30]int
    for i:=0; i< len(nums);i++ {
        nums[i] = i+1
    }
    
    fmt.Println(nums)
    //[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30]
    
    // 区间10-18
    fmt.Println(nums[10:18])
    // [11 12 13 14 15 16 17 18]
    // 中间到尾部
    fmt.Println(nums[15:])
    // [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30]
    // 开头到中间
    fmt.Println(nums[:10])
    // [1 2 3 4 5 6 7 8 9 10]

2表示原有的切片

 fmt.Println(nums[:])
    // [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30]

3重置切片,清空拥有的元素

fmt.Println(nums[0:0])
    // []
    fmt.Println(nums)
    // [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30]

3.2.2 声明切片

每一种类型都可以拥有其切片类型,表示多个类型元素的连续集合 切片类型声明格式: var name []T name 表示切片类型的变量名 T 表示切片类型对应的元素类型 例子

   // 声明字串切片
    var str_slice []string
    // 声明整型
    var int_slice []int
    // 声明一个空切片
    var null_slice = []int{} // null_slice已经是被分配到内存中,但没有元素
    
    fmt.Println(str_slice, int_slice, null_slice)
    // [] [] []
// 输出3个切片大小
    fmt.Println( len(str_slice), len(int_slice), len(null_slice) )
    // 0 0 0
    
    // 切片判断空的结果
    fmt.Println( str_slice == nil)
    fmt.Println( int_slice == nil)
    fmt.Println( null_slice == nil)
    // true
    // true
    // false  null_slice已经是被分配到内存,但没有元素,因此和nill比较时false

切片是动态结构,只能与nil判定相等,不能互相判等时

 

3.2.3 使用make()函数构造切片--动态创建一个切片

make()内建函数动态地创建一个切片 写法: make( []T, size, cap ) T: 切片的元素类型 size: 就是为这个类型分配多少个元素 cap: 预分配的元素数量,设置后不影响size,只是能提前分配空间,降低多次分配空间造成的性能问题

   make_a := make([]int, 2)
    make_b := make([]int, 2, 5)
    fmt.Println(make_a, make_b)
    // [0 0] [0 0]

make_b 的内部存储空间已经分配了5个,但是实际上使用了2个元素 make()函数生成的切片一定发生了内存分配操作 但是给定切片的开始和结束位置的切片只是将新的切片结构指向已经分配好的内存区域 切片不一定经过make()函数声明使用,生成切片,声明后使用append()函数 均可以正常使用切片

3.2.4 使用append()函数为切片动态添加元素

每个一起切片都会指向一个一片内存空间,空间能容纳一定数量的元素 当空间不容纳足够多的元素时,切片就会扩容,容量的扩展规律按容量的2倍扩充,如1,2,4,8,16

 var numbers []int
    
    for i := 0; i<10; i++ {
        numbers = append(numbers, i)
        fmt.Printf("len : %d  cap: %d pointer: %p
", len(numbers), cap(numbers), numbers)
    }
    
    // len : 1  cap: 1 pointer: 0xc000054318
    // len : 2  cap: 2 pointer: 0xc000054330
    // len : 3  cap: 4 pointer: 0xc00006a0a0
    // len : 4  cap: 4 pointer: 0xc00006a0a0
    // len : 5  cap: 8 pointer: 0xc000076100
    // len : 6  cap: 8 pointer: 0xc000076100        
    // len : 7  cap: 8 pointer: 0xc000076100
    // len : 8  cap: 8 pointer: 0xc000076100
    // len : 9  cap: 16 pointer: 0xc000088080
    // len : 10  cap: 16 pointer: 0xc000088080
    
    // 一次性添加很多元素
    var car []string
    // 添加1个元素
    car = append(car, "OldDriver")
    // 添加多个元素
    car = append(car, "apple", "orange", "milk")
    
    // 添加切片
    team := []string{"pig", "Flyingcake", "Chicken"}
    car = append(car, team...)
    
    fmt.Println(car)
    // [OldDriver apple orange milk pig Flyingcake Chicken]

3.2.5 使用copy()函数复制切片元素到另一个切片

格式: copy( destSlice, srcSlice []T) int srcSlice为数据来源切片 destSlice为复制的目标 复制目标的条件要求:

  1. 目标切片不是分配过空间且足够容纳复制的元素

  2. 来源和目标的类型需一样

copy()返回值表示实际发生复制的元素个数

 // 设置元素个数 100
    const elementCount = 100
    // 预分配元素切片
    srcData := make([]int, elementCount)
    // 将切片赋值
    for i := 0; i < elementCount; i++ {
        srcData[i] = i
    }
    // 引用切片数据
    refData := srcData
    // 预分配足够多的元素切片
    copyData := make([]int, elementCount)
    // 将数据复制到新的切片空间中
    copy( copyData, srcData)
    
    // 修改原数据第一个元素
    srcData[0] = 99
    // 打印引用切片的第一个元素
    fmt.Println(refData[0])
    // 99
    // 打印复制切片的第一个和最后一个元素
    fmt.Println(copyData[0], copyData[elementCount-1])
    // 0 99
    
    // 复制原始数据2到6(不包含)
    copy(copyData, srcData[2:6])
    
    for i:=0; i<10;i++ {
        fmt.Printf("%d ", copyData[i])
    }
    //2 3 4 5 4 5 6 7 8 9 

3.2.6 从切片中删除元素

Go语言没有对删除切片元素提供专门的语法或接口,只能利用切片本身特性删除元素 删除元素的本质:以被删除元素为分界点,将前后两个部分的内存重新连接起来

 seq := []string{"one", "two", "three", "four", "five"}
    
    // 指定删除位置
    index := 2
    
    // 查看删除位置之前的元素和之后的元素
    fmt.Println(seq[:index], seq[index + 1:])
    // [one two] [four five]
    
    // 将删除点前后元素量连接起来
    seq = append(seq[:index], seq[index+1:]...)
    fmt.Println(seq)
    // [one two four five]
 

‘…’ 其实是go的一种语法糖。 第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数。 第二个用法是slice可以被打散进行传递

原文地址:https://www.cnblogs.com/smallyi/p/10004645.html