Go复合数据类型

数据和结构体都是聚合类型,它们的值是由内存中其他的值来组成的。
数组是同构类型的—每个数组中的元素都是一致的类型
结构体是异构类型的
数组和结构体都是具有固定内存大小的数据结构
切片和map则是动态的数据结构,他可以通过添加值而增长内存大小

Arrays 数组

数组是由固定长度的零值或特定类型的元素所组成的序列。
因为数组长度固定,所以很少在Go中直接使用
与数组相对的是切片Slice,他可以扩张和收缩,更加通用

数组的每个元素可以通过下面来访问,索引范围为0 ~ (len - 1)
注意:len(array) 返回的是数组元素的个数
默认,一个新的数组变量的元素会被初始化为元素类型的零值,我们可以使用数组字面量来初始化我们想要的值:

    var arr_int [2]int
    var arr_str [2]string

    var arr_str_value = [2]string{"1","2"}

    for index,no := range arr_int {
        fmt.Printf("index = %d and value = %d \n",index,no)
    }

    //输出
    index = 0 and value = 0 
    index = 1 and value = 0 

    for index,no := range arr_str {
        fmt.Printf("index = %d and value = %s \n",index,no)
    }
    //输出
    index = 0 and value =  
    index = 1 and value =  
    for index,no := range arr_str_value {
        fmt.Printf("index = %d and value = %s \n",index,no)
    }
    //输出
    index = 0 and value = 1 
    index = 1 and value = 2 

当初始化数组时候,初始长度可以使用[…]来表示,但是需要在{}放置初始化值,如下:

    arr_int_value := [...]int{1,2}
    for index,no := range arr_int_value {
        fmt.Printf("index = %d and value = %d \n",index,no)
    }

数组的大小也是类型的一部分。因此[3]int 与 [4]int是不同的类型,而数组的大小必须是一个常量表达式,因为数组的长度需要在编译时确定!

    arr01 := [3]int{1,2,3}
    arr01 = [4]int{1,2,3,4} //error

数组除了支持顺序的初始化,还支持索引初始化:

//未在初始化语句中初始化的索引,会被填充以零值(默认值)
arr_index := [...]string{1:"1",4:"4"}

type Currency int

const(
    RMB Currency = iota
    DOLLAR
)

money := [...]string{RMB:"¥",DOLLAR:"$"}

同类型的数组可以进行比较,只有当两个数组的所有元素都相等时,数组才相等。
crypto/sha256包中的函数Sum256函数,会将存储在任意字节切片中的消息,生成sha256密码散列或摘要。生成的摘要是256位的,因此它是[32]byte类型。如果两个摘要是相同的,那么其所对应的消息也是一样的,反之亦然。

    sum256_x := sha256.Sum256([]byte("x"))
    sum256_X := sha256.Sum256([]byte{'X'})
    fmt.Println(sum256_x)
    fmt.Println(sum256_X)
    fmt.Println(sum256_x == sum256_X)
[45 113 22 66 183 38 176 68 1 98 124 169 251 172 50 245 200 83 15 177 144 60 196 219 2 37 135 23 146 26 72 129]
[75 104 171 56 71 254 218 125 108 98 193 251 203 238 191 163 94 171 115 81 237 94 120 244 221 173 234 93 246 75 128 21]
false

当讲数组传递给函数时候,实际是将拷贝传递给了函数,在函数内堆数组的任何改变,并不会反馈到原始数组上。如果需要这种反馈机制,那么可以使用切片或者指针

Slice 切片

切片表示的边长的序列,序列内的元素的类型需一致。
序列的类型为[]T,其中T为元素类型,与数组区别的是没切片不需要有大小

切片是一个轻量级数据结构,他给予了对一个数组的全部或者一个子集的访问,而这个数组也被成为是切片的underlying array[底层数组]

切片有三个组件:
1. point(指针):指针指向切片中第一个元素所对应的底层数组的元素的地址
2. length(长度):即切片的长度
3. capacity(容量):长度不能超过容量,容量则为切片起始位置到底层数组结束位置的元素数量
内置的len()和cap()分别返回长度和容量

多个切片可以共享相同的底层数组,而且所引用的数组中的元素可以相互重叠:

    month := [...]int{0,1,2,3,4,5,6,7,8,9,10,11,12}
    summer := month[6:9] //切片
    //more := summer[:20] ---会报错,因为超过cap
    endlessSummer := summer[:5] //切片小len小于cap,会自动扩展
    fmt.Println(summer)
    fmt.Println(endlessSummer)

字符串的切片操作与[]byte的切片是基本一致的,都写作x[m:n],且都返回一个原始序列的子序列(字符串本身就是字节序列)

    s := "hh哈哈"
    ss := s[2:4]
    b := []byte("xyz哈哈") 
    bb := b[2:5]
    fmt.Println(ss) //�
    fmt.Println(bb) //[122 229 147]

因为切片包含指向底层数组的元素的指针,因此将切片传递给函数,可以允许函数修改底层的数组。换句话说,复制切片只不过是对底层的数组创建一个新的别名:

    nums := [10]int{1,2,3,4,5,6,7,8,9,10}
    reverse(nums[:])
    fmt.Println(nums)
    //[10 9 8 7 6 5 4 3 2 1]

切片的初始化与数组基本相似,只不过切片不需要指定长度。这样会在底层隐式的创建一个合适大小的数组,然后讲切片的指针指向该数组。

    init1 := []int{1,2,3}//顺序初始化[1 2 3]
    init2 := []int{0:0,2:2,3}//索引初始化+顺序初始化[0 0 2 3]

PS;切片无法比较,所以不能使用==或者!=来判断两个切片中的所有元素是否一致
Go函数库中提供了一个bytes.Equal函数来比较两个字节切片([]byte),而对于其他切片类型,则需要展开每一个元素进行比较:

func equals(s1,s2 []string) bool {
    if len(s1) != len(s2) {
        return false
    }
    for index,s := range s1 {
        if s != s2[index] {
            return false
        }
    }
    return true;
}

Slice不支持比较的原因:

  1. Slice的元素是间接引用的,它甚至可以引用自身
  2. 因为Sclice的元素是间接引用的,因此不同的时间,可能元素是不同的值,因为底层的数组是可以被修改的

Slice唯一合法的比较是与nil的比较,零值的Slice的类型就是nil,nil切片没有底层数组,且其length(长度)和capacity(容量)都是0,当然也有非nil的切片的length(长度)和capacity(容量)也是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

So:测试Slice是否为空,需要使用len(sclice) == 0,而不是slice == nil

make函数构造切片:(make会创建一个匿名的底层数组,以我们传递的参数为依据)
1. make(type,length),例如:ints := make([]int, 2)
2. make(type,length,capacity),例如;strings := make([]string, 2, 5)

apend 添加

Go内置了append函数,可以方便我们像切片中插入值

    makes := make([]int, 2, 2)
    fmt.Println(makes,"length = ",len(makes)," cap = ",cap(makes)) //[0,0] length =  2  cap =  2
    makes = append(makes,1)
    fmt.Println(makes,"length = ",len(makes)," cap = ",cap(makes)) // [0,0,1] length =  3  cap =  4
    makes = append(makes,2,3)
    fmt.Println(makes,"length = ",len(makes)," cap = ",cap(makes)) //[0,0,1,2,3] length =  5  cap =  8

Slice原处修改技术

模拟remove操作

func remove (ss []string,index int) []string {

    if index < len(ss) {
        copy(ss[index:],ss[index + 1 :])
        return ss[:len(ss)-1]
    } else {
        return ss
    }

}

猜你喜欢

转载自blog.csdn.net/qq_31179577/article/details/81544274