Learn Go (四) 数组, 切片, 字符串应用和处理

144 阅读4分钟

数组

定义

var, := 关键字; 元素数量写在类型前面

var arr1 [5] int // 中括号数字, 是指定元素个数, 默认元素为 0
arr2 := [3] int {1, 2, 3}
arr3 := [...] int {4, 5, 6, 7, 8} // 让编译器计算元素个数

var grid [4][5] int // 代表 4 行 5 列  二维数组, 只能用 var 声明

遍历

// 方式一
arr3 := [...] int {1, 3, 5, 7, 9}

for i := 0; i < len(arr3); i++ {

    fmt.println(arr3[i])
} 

// 方式二
for i := range arr3 {
   fmt.Println(arr3[i])
}

// 方式三, 输出 i, v 
for i, v := range arr3 {
    fmt.println(i, v)
}

// 只输出 v, 使用 "_" 替换 i 
for _, v := range arr3 {
    fmt.println(v)
}

类型

  • 数组是值类型

    package main
    
    import "fmt"
    
    func printArray(arr [5]int)  {
    
      // 若此处将数组下标为 0 的元素,会重新赋值成100
      arr[0] = 100
      for i, v := range arr {
         fmt.Println(i, v)
      }
    
    }
    
    
    func main() {
      var arr1 [5] int // 中括号数字, 是指定元素个数, 默认元素为 0
      arr2 := [3] int {1, 2, 3}
      arr3 := [...] int {4, 5, 6, 7, 8} // 让编译器计算元素个数
    
      var grid [4][5] int // 二维数组, 只能用 var 声明
    
    
      fmt.Println(arr1, arr2, arr3) // 这里输出的数组还是原来的数组, 没有改变
      fmt.Println(grid)
      printArray(arr3)
    }
    

小结

  • [10] int[20] int 是不同的类型
  • 调用 func f(arr [10] int) 会重新 拷贝 一个数组
  • Go 语言中一般不直接使用数组

切片 slice

概念

Go 语言切片是对数组的抽象。可以理解成数组的 view

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

操作

基操
 package main

 import "fmt"

 // slice 不是值传递,可以看成 视图
 func updateSlice(s []int)  {
    s[0] = 100
 }



 func main() {

    arr := [...] int {0, 1, 2, 3, 4, 5, 6, 7}


    fmt.Println("arr[2:6] = ", arr[2:6])
    fmt.Println("arr[:6] = ", arr[:6])

    s1 := arr[2:]
    fmt.Println("s1 = ", s1)
    s2 := arr[:]
    fmt.Println("s2 = ", s2)

    fmt.Println("After updateSlice(s1)")
    updateSlice(s1)
    fmt.Println(s1)
    fmt.Println(arr)

    fmt.Println("After updateSlice(s2)")
    updateSlice(s2)
    fmt.Println(s2)
    fmt.Println(arr)
    
    
     fmt.Println("Reslice")
     fmt.Println(s2)
     s2 = s2[:5]
     fmt.Println(s2)
     s2 = s2[2:]
     fmt.Println(s2)


 }

 
     // 结果
     arr[2:6] =  [2 3 4 5]
     arr[:6] =  [0 1 2 3 4 5]
     s1 =  [2 3 4 5 6 7]
     s2 =  [0 1 2 3 4 5 6 7]
     After updateSlice(s1)
     [100 3 4 5 6 7]
     [0 1 100 3 4 5 6 7]
     After updateSlice(s2)
     [100 1 100 3 4 5 6 7]
     [100 1 100 3 4 5 6 7]
     Reslice
     [100 1 100 3 4 5 6 7]
     [100 1 100 3 4]
     [100 3 4]

扩展

arr := [...] int {0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]

s2 := s1[3:5]

问 :

  • s1 的值?

  • s2 的值?

    s1 的值 [2 3 4 5]
    s2 的值 [5 6]
    

答 :

s1arr 中取得新数组 [2, 3, 4, 5], 那么 s1 的下标分别是 0, 1, 2, 3; 由于s1arr 的 view, s1arr 存在映射关系. arr 中的元素 6, 7 对应着 s1 不可见下标 4, 5; s2 获取 s13,5 等同于获取 arr 中的 5, 7; 左开右闭 原则, 所以 s2 的值就是 [5, 6]

answer.png

  • 知识点

    image.png

    • slice 可以向后扩展, 不可以向前扩展

    • s[i] 不可以超越 len(s), 向后扩展不可以超越底层数组 cap(s)

添加元素

arr := [...] int {0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:6]

s2 := s1[3:5]

s3 := append(s2, 10)

s4 := append(s3, 11)

s5 := append(s4, 12)

问 : s3, s4, s5 的值为多少? arr的值又变成

结果 :
[2 3 4 5]
[5 6]
[5 6 10]
[5 6 10 11]
[5 6 10 11 12]
  • 关键字 append
  • 添加元素时如果超越 cap, 系统会重新分配更大的底层数组
  • 由于值传递关系, 必须接收 append 的返回值
  • s = append(s, val)
复杂操作
  • 追加

    append(s, v) 第一个参数是追加的 slice, 第二个是 element

    func main () {
        var s [] int // 空的 slice, 默认 0 填充 
        
        for i := 0; i < 100; i++ {
        
            s = append (s, 2 * i + 1)
        }
        
    }
    
    
    
  • 设置指定长度的 slice

    make([]int, 16, 32) 第一个参数创建类型, 第二个参数是长度, 第三个是分配的空间(可选)

    // 建立长度为 16 的 slice
    s2 := make([]int, 16)
    s3 := make([]int, 10, 32)
    
  • 复制 slice

    copy(s1, s2) 第一个参数是要复制的目标slice,第二个参数是源slice

    s1 := [] int {2, 3, 4, 5}
    s2 := make([]int, 16)
    
    copy(s2, s1)
    // 结果
    [2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0], len = 16, cap = 16
    
    
  • 删除一个元素

    没有删除函数, 采取截取, 再拼接的方式实现删除

    s2 = append(s2[:3] , s2[4:]...)
    // 结果
    [2 3 4 0 0 0 0 0 0 0 0 0 0 0 0], len = 15, cap = 16
    
  • 头部 Pop 一个元素

    同样, 也是采取截取的方式

    s2 = s2[1:]
    
    // 结果
    [3 4 0 0 0 0 0 0 0 0 0 0 0 0], len = 14, cap = 15
    
  • 尾部 Pop 一个元素

    同样, 也是采取截取的方式

    s2 = s2[:len(s2) - 1]
    
    // 结果
    [3 4 0 0 0 0 0 0 0 0 0 0 0], len = 13, cap = 15
    

Map

定义

关键字 map[k]v 格式 :

map[K]V, map[K1]map[K2]V

示例

创建
// 示例 一
m := map[string] string {
    "name" : "mouse",
    "course" : "golang",
    "site" : "imooc"
}

// 示例 二
m2 := make(map[string]int) // m2 == empty mpa

// 示例 三
var m3 map[string]int // m3 == nil 
   
应用
  • 循环输出

    // 循环输出 map 
    for k, v := range m {
        fmt.println(k, v)
    }
    
    // 结果, 可以出来 map 的 k 是无序的
    site imooc
    quality good
    name mouse
    course golang
    Getting values
    
  • 获取其中一个值

    // 获取 map 其中一个值
    courseName, ok := m["course"]
    fmt.println(courseName, ok)
    
    // 结果
    golang true
    
  • k 不存在

    // 若获取的 k 不存在, 则返回 空 
    causeName, ok2 := m["cause"]
    fmt.println(causeName, ok2)
    // 结果
    "" false
    // 改写
    if causeName, ok2 := m["cause"]; ok {
        fmt.println(causeName)
    } else {
        fmt.println("key does no exist")
    }
    // 结果
    key does not exist
    
    
  • 删除一个值 delete(), 第一个参数是 map, 第二个参数是要删除的 key

    name, ok := m["name"]
    fmt.println(name, ok)
     
    delete(m, "name")
    // 结果  
    map[course:golang quality:good site:imooc]
    
小结
  • 创建: make(map[string]int)
  • 获取元素 : map[key]
  • key 不存在时, 则取得 Value 类型的初始值, 不会报错
  • 使用 value, ok := m[key] 两个值来判断 key 是否存在
  • 使用 delete(m, key) 删除一个值
  • 使用 range 遍历 key, 或者遍历 key, value
  • 不能保证遍历顺序, 若需要顺序, 可以添加到 slice 再对 slice 排序
  • 使用 len 获得元素个数
  • map 使用哈希表, 必须可以比较相等
  • 除了 slice, map, func 的内建类型都可以作为 key
  • strut 类型不包含上述字段, 也可以作为 key

高级操作

例子 一

寻找最长不含有重复字符的子串

abcabcbb ---> abc

bbbbb ---> b

pwwkew ---> wke

image.png 对于每一个字母 x

  • lastOccurred[x] 不存在, 或者 < start, 无需操作

  • lastOccurred[x] >= start, 则更新 start

  • 更新 lastOccurred[x], 更新 maxLength

    func lengthOfNonRepeatingSubStr(s string) int  {
       lastOccurred := make(map[byte]int)
       start := 0
       maxLength := 0
       for i, ch := range []byte(s){
    
          lastI, ok := lastOccurred[ch]
          if ok && lastI >= start {
             start = lastI + 1
          }
          if i - start + 1 > maxLength {
             maxLength = i - start + 1
          }
          lastOccurred[ch] = i
       }
       return maxLength
    
    }
    

    计算中文等汉字会出现问题

字符和字符串处理

rune 相当于 Go 的 char

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "Yes中文!"
    fmt.println(s) // 输出结果: 10
    
    for _, b := range []byte(s) {
        fmt.printf("%X ", b)  // 输出十六进制
    }   
}

// 引入 unicode/utf8 包
fmt.Println("Rune count", utf8.RuneCountInString(s)) // 输出6

bytes := []byte(s)
for len(bytes) > 0 {
    ch, size := utf8.DecodeRune(bytes)
    bytes = bytes[size:]
    
    fmt.printf("%c ", ch) // 输出 Y e s 中 文 !
}

// 使用 rune, 可直接输出汉字
for i, ch := range []rune(s) {
    fmt.printf("(%d, %c)", i, ch) // 输出 (0, Y)(1, e)(2, s)(3, 中)(4, 文)(5, !)
}

再对上面的例子 一, 遗留的问题进行修改

func lengthOfNonRepeatingSubStr(s string) int  {
   // 将 byte 修改成 rune 即可解决 
   lastOccurred := make(map[rune]int) 
   start := 0
   maxLength := 0
   // 将 byte 修改成 rune 即可解决
   for i, ch := range []rune(s){

      lastI, ok := lastOccurred[ch]
      if ok && lastI >= start {
         start = lastI + 1
      }
      if i - start + 1 > maxLength {
         maxLength = i - start + 1
      }
      lastOccurred[ch] = i
   }
   return maxLength
   
}
  • 其他字符操作

    推荐使用 strings package 来处理, 里面有很多函数, 多多尝试就会用了