# Go基础编程：数组、切片、字符串

2020-11-10 16:01:10 一夕烟云

#### 数组

##### 定义
``````var a [4]int                   //定义长度4，元素类型为int,值为  0 0 0 0
var b = [5]int{1, 2, 3, 4}     //定义长度5，元素类型为int,值为  1 2 3 4 0
var c = [...]int{1, 2, 3, 4}   //定义长度4，元素类型为int,值为  1 2 3 4
var d = [4]int{1: 2, 3}        //定义长度4，元素类型为int,值为  0 2 3 0
var e [4]int = [4]int{1: 2, 3} //定义长度4，元素类型为int,值为  0 2 3 0
var f  = [2]string{"hello","world"} //定义长度2，元素类型为string,值为  "hello","world"``````

``var b = [5]int{1, 2, 3, 4}``

#### 基本操作

``````//数组下标是从0开始的。如果下标在数组合法范围之外，则触发访问越界，会panic
var arr = [5]int{1, 2, 3, 4}
fmt.Println(arr)       //打印全部 [1 2 3 4 0]
fmt.Println(arr[1])  //打印下标为1(第二个元素) 2
fmt.Println(arr[5])  //invalid array index 5 (out of bounds for 5-element array)``````

`for`循环来迭代数组

``````var arr = [5]int{1, 2, 3, 4}
for range arr {
fmt.Println("hello world")
}
//1、hello hello hello hello hello

for k := range arr {
fmt.Println(k)
}
//2、打印下标：
//0 1 2 3 4

for k, v := range arr {
fmt.Println(k, v)
}
//3、key=0,val=1 key=1,val=2 key=2,val=3 key=3,val=4 key=4,val=0
//不想打印k，可以用 "_" 代替k,表示忽略k

for i := 0; i < len(arr); i++ {
fmt.Println(i, arr[i])
}
//4、key=0,val=1 key=1,val=2 key=2,val=3 key=3,val=4 key=4,val=0 ``````

``````//数组长度相同时：
var a = [5]int{1, 2, 3, 4}
var b = [5]int{}
b = a
fmt.Println(b) //[1 2 3 4 0]
fmt.Println(b != a) //false

//数组长度不相同时：
var a = [5]int{1, 2, 3, 4}
var b = [6]int{}
b = a //cannot use a (type [5]int) as type [6]int in assignment
fmt.Println(b != a) //invalid operation: b != a (mismatched types [6]int and [5]int)

//想把a的值给b只能通过下标一个个改
for i := 0; i < len(a); i++ {
b[i] = a[i]
}
fmt.Println(b) //[1 2 3 4 0 0]
``````

``````var a = [3][2]int{{}, {}, {}} //[[0 0] [0 0] [0 0]]
var b = [3][2]int{[2]int{}, [2]int{}, [2]int{}} //[[0 0] [0 0] [0 0]]
var c = [3][2]int{{1}, {2}, {3}} //[[1 0] [2 0] [3 0]]
var d = [3][2]int{{1, 2}, {2, 2}, {3, 2}} //[[1 2] [2 2] [3 2]]
``````

``````赋值
var arr [3][2]int
fmt.Println(arr) //[[0 0] [0 0] [0 0]]
arr[0][1] = 2
arr[2][0] = 3
fmt.Println(arr) //[[0 2] [0 0] [3 0]]``````

#### 字符串

Go字符串在底层`reflect.StringHeader`结构:

``````type StringHeader struct {
Data uintptr
Len int
}``````

Go使用双引号`""`或反引号```包起来的表示是字符串内容，注意不能用单引号`'`,单引号表示的是字节(byte)，即UTF-8对应的编码

``````var str string
var str1 = "hello"
var str2 string = "world"
str3 := "hello world"
fmt.Println(str) //
fmt.Println(str1) //hello
fmt.Println(str2)//world
fmt.Println(str3)//hello world``````

Go字符串的初始值为空字符串`""`,注意`" "`不是空字符串，这个表示空格字符串，占一个字节，对应ASCLL码的00100000B(32H)。

• \n：换行符
• \r：回车符
• \t：tab 键
• \u 或 U：Unicode 字符
• \\：反斜杠自身

``````str := "中国abc"
for k, v := range str {
fmt.Println(k, v)
}
//0 20013
//3 22269
//6 97
//7 98
//8 99``````

``````str := "abcdef"
fmt.Println(str[1]) //通过下标访问（读）： 98
str[1] = "h" // 通过下标改（写）：cannot assign to str[1] ``````

``````str := "abcdef"
fmt.Println(str) //abcdef
str = "hello world"
fmt.Println(str) //hello world``````

``````package main

import (
"fmt"
"reflect"
"unsafe"
)

func main() {
str := "hello "
str = "world"
}

}``````

1. `+`号拼接，会产生开销

``````str1 := "hello "
str2 := "world"
str := str1 + str2
fmt.Println(str) //hello world``````
2. fmt包下的打印函数,效率不高

``````str1 := "hello "
str2 := "world"
str := fmt.Sprintf("%s%s", str1, str2)
fmt.Println(str) //hello world``````
3. `strings.Join`表示以什么字符串拼接字符串数组，在已有一个数组的情况下，这种效率会很高，如果没有的话效率也不高。

``````str1 := "hello "
str2 := "world"
str3 := []string{str1, str2}
str := strings.Join(str3, "")
fmt.Println(str) //hello world``````
4. 字节缓冲`bytes.Buffer`这种方法的性能就要大大优于上面的了

``````str1 := "hello "
str2 := "world"
var by bytes.Buffer
by.WriteString(str1)
by.WriteString(str2)
fmt.Println(by.String())``````
5. `strings.Builder`性能和`bytes.Buffer`一样

``````str1 := "hello "
str2 := "world"
var by strings.Builder
by.WriteString(str1)
by.WriteString(str2)
fmt.Println(by.String())``````

#### 切片

Go字符串在底层`reflect.SliceHeader`结构:

``````type SliceHeader struct {
Data uintptr
Len int
Cap int
}``````

`reflect.SliceHeader`结构体组成有三部分：一部分是切片数据指针，另一部分是切片长度，最后一部分是切片容量。

``````var arr1 []int // nil切片
var arr2 []int = []int{1, 2}// 初始值为[1 2]
var arr3 = []int{1, 2, 3}//初始值为[1 2 3]
var arr4 = arr3[1:]//arr3的部分 初始值为[2 3]
var arr5 = make([]int, 3) // 有3个元素的切片, len和cap都为3
var arr6 = make([]int, 2, 3)// 有2个元素的切片, len为2, cap为3``````

``````package main

import (
"fmt"
"reflect"
"unsafe"
)

func main() {
var arr1 = []int{1, 2, 3}
var arr2 = make([]int, 2, 3)
fmt.Println(len(arr2), cap(arr2)) //2 3
fmt.Println(len(arr1), cap(arr1)) //3 3
arr1 = append(arr1, 4)
fmt.Println(len(arr1), cap(arr1)) //4 6
}
//获取切片数据地址
}
``````

``````package main

import (
"fmt"
)

func main() {

var arr = []int{1, 2, 3}
//打印下标为1的元素：
fmt.Println(arr[1]) // 2
//获取全部元素：
fmt.Println(arr) //[1 2 3]

// 更改下标为2的元素
arr[2] = 5
fmt.Println(arr[2]) //5

//不能越界访问
fmt.Println(arr[3]) //panic: runtime error: index out of range

}``````

``````package main

import (
"fmt"
)

func main() {

var arr = []int{1, 2, 3}
fmt.Println(arr) //[1 2 3]
// 新增一个元素
arr = append(arr, 4)
fmt.Println(arr) //[1 2 3 4]
// 新增2个元素
arr = append(arr, 5, 6)
fmt.Println(arr) //[1 2 3 4 5 6]
// 新增多个个元素，语法糖(arr1...) 表示把arr1 从头到尾一样一一赋值
var arr1 = []int{20, 30}
arr = append(arr, arr1...)
fmt.Println(arr) //[1 2 3 4 5 6 20 30]

}``````

``````package main

import (
"fmt"
"reflect"
"unsafe"
)

func main() {

var arr = []int{1, 2, 3, 4, 5, 6, 20, 30}
fmt.Println(arr) //[1 2 3 4 5 6 20 30]
-------------------------浅拷贝----------------------------
// 赋值
arr1 := arr
fmt.Println(arr1) //[1 2 3 4 5 6 20 30]
// 截取从下标2及后面的部分
arr1 = arr[2:]
fmt.Println(arr1) //[3 4 5 6 20 30]
// 截取从下标5前面的部分
arr1 = arr[:5]
fmt.Println(arr1) //[1 2 3 4 5]
// 截取从下标2-5的部分
arr1 = arr[2:5]
fmt.Println(arr1) //[3 4 5]

// 切片是引用型数据，这样截取数据实质上是数据指针指向内存地址的改变
// 故其它在这内存地址上的切片改变，原切片的数据会改变，如下：
arr1[1] = 88
fmt.Println(arr) //[1 2 3 88 5 6 20 30]

-------------------------深拷贝----------------------------
// 如果切片本来是引用另一个切片，但元素增加超过切片容量后，会从新开辟空间，对原数据不影响（指向数据的指针不同）
var arr2 = []int{1, 2, 3}
fmt.Println(arr2)            //[1 2 3]
arr3 := arr2
fmt.Println(arr3)            //[1 2 3]
arr3 = append(arr3, 4, 5, 6) //新增元素超过容量，重新开辟空间
fmt.Println(arr2)            //[1 2 3]
fmt.Println(arr3)            //[1 2 3 4 5 6]

// 使用内置函数copy(),拷贝一份数据，这种修改数据也对原数据没影响
// copy()和切片扩容后，都是对原数据切片整个复制，不存在对原数据内存地址的引用，这种叫深拷贝，修改对原数据影响没半毛钱关系
var arr4 = make([]int, len(arr))
copy(arr4, arr)
arr4[1] = 100
fmt.Println(arr)  //[1 2 3 88 5 6 20 30]
fmt.Println(arr4) //[1 100 3 88 5 6 20 30]

}

}``````

``````package main

import (
"fmt"
)

func main() {
var arr = []int{1, 2, 3, 4, 5, 6, 20, 30}
for range arr {
fmt.Printf("%s ", "a") // a a a a a a a a
}
fmt.Println("")
for k := range arr {
fmt.Printf("%d ", arr[k]) //1 2 3 4 5 6 20 30
}
fmt.Println("")

for k, v := range arr {
fmt.Printf("%d=>%d ", k, v) //0=>1 1=>2 2=>3 3=>4 4=>5 5=>6 6=>20 7=>30
}
fmt.Println("")
for i := 0; i < len(arr); i++ {
fmt.Printf("%d ", arr[i]) //1 2 3 4 5 6 20 30
}

}``````

https://segmentfault.com/a/1190000037787363