1.数组
-
数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。 基本语法:
// 数组长度必须是常量,且是类型的组成部分,一旦定义,长度不变。
var a [3]int
-
数组可以通过下标进行访问,下标从0开始,最后一个元素下标是:len-1。
// 遍历方式
for i:=0;i<len(Arr);i++ {
}
for index,v := range Arr {
}
-
如果下标在数组的合法范围之外,则触发访问越界,会panic
var arr = [4]string{"1","2","3","4"}
fmt.Println(arr[5])
// 错误信息: invalid array index 5 (out of bounds for 4-element array)
-
数组的初始化:
package main
import "fmt"
//数组相关内容
func main() {
var a [3]int
var b [4]int
//注意a与b不能互相赋值,长度不同
fmt.Println(a, b)
//数组的初始化
//1定义时使用初始值列表的方式初始化
var cityArray = [4]string{"北京", "上海", "广州", "深圳"}
fmt.Println(cityArray)
fmt.Println(cityArray[3])
//2.编译器推导数组的长度
var boolArray = [...]bool{true, false, true, false, false}
fmt.Println(boolArray)
//3.使用索引值方式初始化
var langeArray = [...]string{1: "Golang", 3: "Python", 7: "Java"}
fmt.Println(langeArray)
fmt.Printf("%T\n", langeArray)
}
-
数组内定义结构体
d := [...]struct {
name string
age uint16
}{
// 注意最后一行要加逗号,否则报错:syntax error: unexpected newline, expecting comma or {
{"u1",123},
{"u2",456},
{"u3",789},
}
// [{u1 123} {u2 456} {u3 789}]
fmt.Println(d)
-
数组的遍历:
var cityArray = [4]string{"北京", "上海", "广州", "深圳"}
//1.for 循环遍历
for i := 0; i < len(cityArray); i++ {
fmt.Println(cityArray[i])
}
var cityArray = [4]string{"北京", "上海", "广州", "深圳"}
for index, value := range cityArray {
fmt.Println(index, value)
}
-
二维数组:
-
注意:多维数组,只有最外层可以用[...]让编译器自己识别编译,内层不能使用。
//定义一个多维数组
cityArray := [...][2]string{
{"北京", "西安"},
{"上海", "重庆"},
{"杭州", "成都"},
{"广州", "深圳"},
}
//打印杭州
fmt.Println(cityArray[2][0])
//遍历循环二维数组,
for _, value1 := range cityArray {
for _, value2 := range value1 {
fmt.Println(value2)
}
}
// demo2
package main
import "fmt"
func main() {
// 这里需要注意,第二个维度的不能使用"..."
var arr1 [2][3]int = [...][3]int{{1,2,3},{4,5,6}}
// [[1 2 3] [4 5 6]]
fmt.Println(arr1)
}
-
数组类型
- 数组是值类型,赋值和传参会赋值整个数组,而不是指针。所以改变副本的值,不会改变本身的值。
var arr = [4]string{"1","2","3","4"}
arr2 := arr
arr2[3] = "10"
fmt.Println(arr,arr2)
// [1 2 3 4] [1 2 3 10]
package main
import "fmt"
//数组相关内容
func main() {
//数组 是 值类型
x := [3]int{1, 2, 3}
fmt.Println(x)//[1 2 3]
f1(x)
y:= x
y[0] = 1000
fmt.Println(x)//[1 2 3]
}
func f1(a [3]int) {
a[0] = 100
}
//前后打印结果一样,数组是值类型,无论是把它当参数传到函数里,还是做一个变量赋值,它都是把数组的值完整拷贝一份再复制给变量
//无论是一维数组,还是二维数组,还是给数组里的值赋值。它都不会改变
-
因数组是值拷贝,会造成性能问题,通常会建议使用slice,或数组指针
package main
import "fmt"
func test(x [2]int) {
// &x
fmt.Printf("x: %p\n",&x)
x[1] = 1000
}
func main() {
a := [2]int{}
fmt.Printf("a:%p\n",&a)
test(a)
// a:0x1104a058
// x: 0x1104a078
// [0 0]
}
func printArr(arr *[5]int) {
arr[0] = 10
}
func main() {
var arr1 [5]int
printArr(&arr1)
// [10 0 0 0 0]
fmt.Println(arr1)
}
-
通过len和cap返回数组长度
a := [2]int{}
println(len(a),cap(a))
// 2 2
求数组[1,3,5,7,8]的所有元素和
package main
import "fmt"
func main() {
// 练习题:
var result int
var sumList = [5]int{1, 3, 5, 7, 8}
for i := 0; i < len(sumList); i++ {
result += sumList[i]
}
fmt.Println(result)
}
找出数组中和为指定两个元素的下标,比如[1,3,5,7,8]和为8的两个元素下标分别为(0,3)和(1,2)
package main
import "fmt"
func main() {
var sumList = [5]int{1, 3, 5, 7, 8}
for index, v1 := range sumList {
for temp, v2 := range sumList {
if index < temp && v1+v2 == 8 {
fmt.Println(index, temp)
}
}
}
}
package main
import (
"fmt"
"math/rand"
"time"
)
func sumArr(a [10]int) int {
var sum int = 0
for _,value := range a {
sum += value
}
return sum
}
func main() {
// 设置随机数种子, 可以保证每次随机都是随机的
rand.Seed(time.Now().Unix())
var b [10]int
for i:=0;i<len(b);i++ {
// 生成一个0-1000随机数
b[i] = rand.Intn(1000)
}
sum := sumArr(b)
fmt.Printf("sum=%d\n",sum)
}
-
找出数组中和为给定值的两个元素的下标,例如数组[1,3,5,8,7],找出两个元素之和等于8的下标分别是(0,4)和(1,2)
func testArr(arr [5]int, num int) {
for i:=0;i<len(arr);i++ {
for j:=i+1;j<len(arr);j++ {
if arr[i]+arr[j] == num {
fmt.Printf("(%d,%d)\n",i,j)
}
}
}
}
func main() {
b := [5]int{1,3,5,8,7}
testArr(b,8)
}
2.切片
-
首先slice切片并不是数组或数组指针,它用过内部指针和相关属性引用数组片段,从而实现变长方案。
1.切片:切片是数组的一个引用,所以切片是引用类型,但自身是结构体,值拷贝传递
2.切片的长度可以改变,因此切片是一个可变数组
3.切片遍历方式和数组一样,可以用len求长度,表示可用元素数量,读写操作不能超过该限制。
4.cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。。
5.切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
6.如果 slice == nil,那么 len、cap 结果都等于 0。
最终:
切片是一个拥有相同类型元素的可变长度的序列,它基于数组类型做的一层封装,灵活度高,支持自动扩容,切片是一个引用类型,它的内部结构包含地址,长度和容量。切片一般用于快速地操作一块数据集合。
-
定义一个切片
package main
import "fmt"
//生成切片(slice)
func main() {
var a []string
var b []int
var c = []bool{false, true}
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
-
切片的长度和容量
- 切片拥有自己长度和容量,我们可以通过使用内置len函数求长度,使用内置cap()函数求切片容量。
-
基于数组切片
package main
import "fmt"
//生成切片(slice)
func main() {
//基于数组得到切片
a := [5]int{22, 23, 24, 25, 26}
b := a[1:4]
fmt.Println(b)
//打印类型
fmt.Printf("%T\n", b)
}
-
make函数构造切片
package main
import "fmt"
//生成切片(slice)
func main() {
//make函数构造函数
//d为一个元素个数为5,容量为10的切片
d := make([]int, 5, 10)
fmt.Println(d)
fmt.Printf("%T\n", d)
//通过len()函数获取切片的长度
fmt.Println(len(d))
//通过cap()函数获取切片的容量
fmt.Println(cap(d))
//[0 0 0 0 0]
//[]int
//5
//10
-
面试题:
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:]
fmt.Println(s1)//[2 3 4 5 6 7]
//修改
s1[0] = 100
//是view的操作(切片映射数组),不同于python,python切片相当于单复制一份表
fmt.Println(s1)//[100 3 4 5 6 7]
fmt.Println(arr)//[0 1 100 3 4 5 6 7]
s2 := arr[2:6]
fmt.Println(s2) //[100 3 4 5]
//前面不能取定死,可以从原数组往后面取(通过映射),超过了原数组长度会报错。但append不会报错
// 错误信息:invalid slice index 10 (out of bounds for 5-element array)
s3 := s2[3:5]
fmt.Println(s3) //[5 6]
}
-
切片初始化
全局:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最后一个元素
局部:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]
slice7 := arr[start:]
slice8 := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
-
切片原理:
-
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)
-
举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7} ,切片s1 := a[:5] ,相应示意图如下。
-
nil
- 切片之间不能直接比较,我们不能使用 == 操作符来判断两个切片是否含有全部相等元素,切片唯一合法的比较操作是和nil比较,一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0,但是我们不能说一个长度和容量为都是0的切片一定是nil.
package main
import "fmt"
//生成切片(slice)
func main() {
var a []int //声明int类型切片
var b = []int{} //声明并且初始化,会再底层创建数组与其对应
c := make([]int, 0)
if a == nil {
fmt.Println("a是一个nil")
}
fmt.Println(a, len(a), cap(a))
if b == nil {
fmt.Println("b是一个nil")
}
fmt.Println(b, len(b), cap(b))
if c == nil {
fmt.Println("c是一个nil")
}
fmt.Println(c, len(c), cap(c))
}
package main
import "fmt"
//生成切片(slice)
func main() {
//切片的赋值拷贝
a := make([]int, 3)
b := a
b[0] = 100
fmt.Println(a)
fmt.Println(b)
}
//[100,0,0]
//[100,0,0]
// 访问底层数组元素指针
s:= []int{0,1,2,3}
p := &[2]
*p += 100
fmt.Println(s) // 102
func main() {
data := [][]int {
[]int{1,2,3},
[]int{100,200},
[]int{11,22,33,44},
}
// [[1 2 3] [100 200] [11 22 33 44]]
fmt.Println(data)
}
- 可直接修改struct array /slice 成员
package main
import "fmt"
func main() {
d := [5]struct{
x int
}{}
d[1].x = 10
d[2].x = 20
// [{0} {10} {20} {0} {0}]
//
// 0x11010320,0x11010320
fmt.Println(d)
fmt.Printf("%p,%p\n",&d,&d[0])
}
package main
import "fmt"
func main() {
//切片的遍历
//索引遍历
a := []int{1, 2, 3, 4, 5}
for i := 0; i < len(a); i++ {
fmt.Println(i, a[i])
}
//for...range遍历
for index, value := range a {
fmt.Println(index, value)
}
}
- 切片的扩容
- append 函数将元素值追加到数组的最后并返回数组
- 当数组容量不满足继续放入元素,会发生扩容现象。切片numSlice的容量按照1,2,4,8,16这样规则自动进行扩容,每次扩容都是扩容前2倍
package main
import "fmt"
//生成切片(slice)
func main() {
//切片要初始化后才能使用
var a []int //此时并没有申请内存
for i := 0; i < 10; i++ {
a = append(a, i) //需要变量来接收,因为你不知道原来数组是否会扩容
fmt.Printf("%v len:%d cap:%d ptr:%p\n", a, len(a), cap(a), a)
}
}
// [0] len:1 cap:2 ptr:0x11010080
// [0 1] len:2 cap:2 ptr:0x11010080
// [0 1 2] len:3 cap:4 ptr:0x110100d0
// [0 1 2 3] len:4 cap:4 ptr:0x110100d0
// [0 1 2 3 4] len:5 cap:8 ptr:0x1100e3a0
// [0 1 2 3 4 5] len:6 cap:8 ptr:0x1100e3a0
// [0 1 2 3 4 5 6] len:7 cap:8 ptr:0x1100e3a0
// [0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0x1100e3a0
// [0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0x1100c280
// [0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0x1100c280
package main
import "fmt"
//生成切片(slice)
func main() {
//切片要初始化后才能使用
var a []int //此时并没有申请内存
a = append(a,1,2,3,4,5)
b:=[]int{11,12,13}
a = append(a,b...)//将切片b加入到里面必须使用...
fmt.Println(a)
}
[1 2 3 4 5 11 12 13 14]
-
切片区别python对比go
#python
a = [1,2,3,4,5,6]
t = a[0:4]
t[0] = 100
print(t)#[100, 2, 3, 4]
print(a)#[1, 2, 3, 4, 5, 6]
p = a[0:5]
print(p)#[1, 2, 3, 4, 5]
p.append(1000)
print(p)#[1, 2, 3, 4, 5, 1000]
print(a)#[1, 2, 3, 4, 5, 6]
#python切片后赋值数据,做更改,增添,都不会影响源数据以及以后切片数据,像深拷贝
#go
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:]
fmt.Println(s1)//[2 3 4 5 6 7]
//修改
s1[0] = 100
//是view的操作(切片映射数组),不同于python,python切片相当于单复制一份表
fmt.Println(s1)//[100 3 4 5 6 7]
fmt.Println(arr)//[0 1 100 3 4 5 6 7]
s2 := arr[2:6]
fmt.Println(s2) //[100 3 4 5]
//前面不能不取定死,可以从原数组往后面取(通过映射),超过了原数组长度会报错。
s3 := s2[3:5]
fmt.Println(s3) //[5 6]
}
#go语言,切片相当于映射数组,当切片更改数据,源数组也会更改,后续切片数据也会更改。
-
总结:
- 首先,如果新申请容量(cap) 大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)
- 否则判断,如果旧切片长度小于1024,则最终容量(newcap)就是旧容量的两倍,即(newcap= doublecap)
- 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap.for{newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
- 如果最终容量(cap)计算值溢出,则最终容量(cap) 就是新申请容量(cap)
!需要注意的是:切片扩容还会根据切片中元素的类型不同而做不同的处理,比如int和string类型的处理方式就不一样。
-
copy函数应用
package main
import "fmt"
func main() {
//切片的copy
a := []int{1, 2, 3, 4, 5}
b := make([]int, 5, 5)
c := b//c为b的赋值,c指向的地址与b一样
copy(b, a)//b切片为从a拷贝过来的,指向新的地址
b[0] = 100
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
//所以当b第0个元素发生变化,c也跟着变化,而a不发生变化
//[1 2 3 4 5]
//[100 2 3 4 5]
//[100 2 3 4 5]
-
copy 函数在两个slice间复制数据,复制长度以len小的为准,两个slice可指向同一底层数组,允许元素区间重叠
func main() {
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("array data : ", data)
s1 := data[8:]
s2 := data[:5]
fmt.Printf("slice s1 : %v\n", s1)
fmt.Printf("slice s2 : %v\n", s2)
// 将s1元素拷贝到s2上,并覆盖
copy(s2, s1)
fmt.Printf("copied slice s1 : %v\n", s1)
fmt.Printf("copied slice s2 : %v\n", s2)
fmt.Println("last array data : ", data)
}
// array data : [0 1 2 3 4 5 6 7 8 9]
// slice s1 : [8 9]
// slice s2 : [0 1 2 3 4]
// copied slice s1 : [8 9]
// copied slice s2 : [8 9 2 3 4]
// last array data : [8 9 2 3 4 5 6 7 8 9]
-
切片resize,调整大小
var a = []int{1,3,4,5}
fmt.Printf("%v,%v\n",a,len(a))
b := a[1:2]
fmt.Printf("%v,%v\n",b,len(b))
c := b[0:3]
fmt.Printf("%v,%v\n",c,len(c))
// [1 3 4 5],4
// [3],1
// [3 4 5],3
-
切片元素的删除
//从切片a中删除"青岛"
package main
import "fmt"
func main() {
//切片删除元素
a := []string{"北京", "上海", "青岛", "深圳"}
a = append(a[0:2], a[3:]...)
fmt.Println(a)
}
//[北京 上海 深圳]
练习题
//1.请写出下面代码的输出结果
func main() {
var a = make([]string,5,10)//此时切片容量10,里面已经有5个空格
for i:=0;i<10;i++{
a.append(a,fmt.Sprintf("%v",i))//当添加元素0,1,2,3,4此时a容量已经满了,而append恰好能对其进行扩容
}
fmt.Println(a)
}
//最后输出结果:
//[ 0 1 2 3 4 5 6 7 8 9]
//2.排序题
package main
import (
"fmt"
"sort"
)
func main() {
//切片删除元素
// a := []string{"北京", "上海", "青岛", "深圳"}
// a = append(a[0:2], a[3:]...)
// fmt.Println(a)
var a = [...]int{3, 7, 8, 1, 9}//a为int类型数组
sort.Ints(a[:])//接收参数为int类型切片。其实a[:]得到的切片,指向底层数组a,对切片排序,相当于对数组a排序
fmt.Println(a)
}
|
请发表评论