go 数组 字符串 切片

数组

数组定义方式

var a [3]int                    // 定义长度为3的int型数组, 元素全部为0
var b = [...]int{1, 2, 3}       // 定义长度为3的int型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2}    // 定义长度为3的int型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6
  • 数组类型包含基础类型和数组长度
  • 数组复制是整体复制
  • 数组名称代表整个数组而不是数组起始地址
  • 可以用&取起始地址,传参,防止大量复制

字符串

  • 不可变字节序列,是一个只读的字节数组
  • 长度不是字符串类型的一部分
  • 默认编码是utf8, for range 语法不支持非UTF8编码字符串的遍历
  • 字符串复制不会涉及底层数组复制,参考下面字符串的结构体, data指针和len长度的复制
  • len(s)求的是[]byte的长度
  • 字符串底层[]byte, rune底层int32
  • []rune 是一个 []int32, rune表示每个Unicode码点,目前只用了21位
type StringHeader struct {
        Data uintptr
        Len  int
}

切片

切片定义

var (
        a []int               // nil切片, 和 nil 相等, 一般用来表示一个不存在的切片
        b = []int{}           // 空切片, 和 nil 不相等, 一般用来表示一个空的集合
        c = []int{1, 2, 3}    // 有3个元素的切片, len和cap都为3
        d = c[:2]             // 有2个元素的切片, len为2, cap为3
        e = c[0:2:cap(c)]     // 有2个元素的切片, len为2, cap为3
        f = c[:0]             // 有0个元素的切片, len为0, cap为3
        g = make([]int, 3)    // 有3个元素的切片, len和cap都为3
        h = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
        i = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
)

切片遍历方式

for i := range a {
                fmt.Printf("a[%d]: %d\n", i, a[i])
        }
        for i, v := range b {
                fmt.Printf("b[%d]: %d\n", i, v)
        }
        for i := 0; i < len(c); i++ {
                fmt.Printf("c[%d]: %d\n", i, c[i])
        }
  • 切片复制与字符串一样, 复制切片头信息reflect.SliceHeader

  • append函数和copy函数组合使用

  • append(s1, s2...), s2自动解包,将s2切片内数据复制到s1

  • 切片高效的要点是要降低内存分配的次数,尽量保证append操作不会超出cap的容量

  • 避免切片内存泄漏

    • func FindPhoneNumber(filename string) []byte {
              b, _ := ioutil.ReadFile(filename)
              b = regexp.MustCompile("[0-9]+").Find(b)
              return append([]byte{}, b...)
      } //一个小的需求可能导致需要长时间保存整个文件数据,可以将结果复制到一个新的切片
      
    • var a []*int{ ... }
      a[len(a)-1] = nil // GC回收最后一个元素内存 
      a = a[:len(a)-1]  // 从切片删除最后一个元素  **若没有第二局,被删除的元素依然被底层数组引用,                                       //从而导致不能及时被GC回收
      
  • 为了安全,当两个切片类型[]T[]Y的底层原始切片类型不同时,Go语言是无法直接转换类型的。可以使用反射进行转换,前提是保证转化的数据安全有效