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

数组

定义

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](https://upload-images.jianshu.io/upload_images/19614659-b84b90b9531acc90.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- `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 来处理, 里面有很多函数, 多多尝试就会用了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359

推荐阅读更多精彩内容