go语言切片

1.切片快速入门案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* @Author: Administrator
* @Description:
* @File: slice
* @Version: 1.13.4
* @Date: 2020/4/18 4:36
*/

package main
import "fmt"
func main(){

//演示切片的基本使用
var intArry [5]int = [...]int{1,22,33,66,99}
//声明定义一个切片
// slice = intArry[1:3]
//1. slice 就是切片的英文名称
//2. intArr[1:3] 表示slice 引用倒了intArry这个数值
//3.引用intArr数组的起始下标为 1,最后的下标为3(但不包括3)
slice := intArry[1:3]
fmt.Println("intArry=",intArry)
fmt.Println("slice 的元素是 =",slice)
fmt.Println("slice 的容量=",cap(slice)) //切片的容量是可以动态变化的,可以使用内置函数cap来查看


}

我们来看一下切片在内存中的布局以及它是如何引用的

img

2.切片的3种创建方式

方式1: 定义一个切片,然后让切片去引用一个已经创建好的数组 比如前面的(切片快速入门)案例就是这样的。

方式2:

第二种方式:通过make来创建切片.

基本语法 var切片名称 [] type = make([]type,len,[cap])

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"

func main(){

//演示切片make的使用
var slice []float64 = make([]float64,5,10)
slice[1] = 10
slice[3] = 20
//对于切片,必须make使用。
fmt.Println("slice的size=",len(slice))
fmt.Println("slice的cap=",cap(slice))
}

对于make而言,make创建的数组对外是不可见的,这个数组没有名称,但它的基本结构体依然是一样的,要访问make数组只能通过slice的方式进行操作,然而和方式1对比的区别是,方式1的数组是事先存在的,程序员是看见的。

方式3:

定义一个切片,之间就指定具体的数组,使用原理类似于make的方式。

1
2
3
4
func main(){
var slice []int = []int {1,3,5}
fmt.Println(slice)
}

3.切片的遍历

切片的遍历和数组一样,也有两种方式

  1. for循环常规方式遍历
  2. for-range 结构遍历切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @Author: Administrator
* @Description:
* @File: ForSlice
* @Version: 1.13.4
* @Date: 2020/4/20 3:46
*/

package main
import "fmt"
func main(){
//使用常规的for循环遍历切片
//第一种方式:
var arry [5]int = [...]int {10,20,30,40,50}
slice:= arry[1:4]
for i:=0; i<len(slice); i++{
fmt.Printf("slice[%v]=%v",i,slice[i])
}

//使用for--range 方式遍历切片
for i,v:=range slice {
fmt.Printf("i=%v v=%v\n",i,v)
}

}

4.append内置函数在切片的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @Author: Administrator
* @Description:
* @File: append
* @Version: 1.13.4
* @Date: 2020/4/21 14:18
*/

package main
import "fmt"
func main(){

//用append内置函数,可以对切片进行动态追击元素
var slice []int =[]int{100,200,300}
//通过append直接给slice追加元素
slice = append(slice,400,500,600)
fmt.Println("slice=",slice)//100 200 300 400 500 600
//当然slice还可以进行追加切片元素
slice = append(slice,slice...)
fmt.Println("newslice=",slice)//100 200 300 400 500 600 100 200 300 400 500 600]

}

//注意的是,定义切片时明确了数据类型,那使用append追加时必须是相同的数据类型,不能说切片定义时设定了int类型,在append的时候追加了个float类型,这是不许的。

5.copy内置函数在切片的应用

在进行数据copy时,内存开辟的数据空间是独立的,也就是说,对A的数据进行拷贝,copy到变量B

之后对A的数据进行修改操作时,AB各自不会同时变化,不会影响到B,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @Author: Administrator
* @Description:
* @File: copy
* @Version: 1.13.4
* @Date: 2020/4/21 14:33
*/

package main
import "fmt"
func main(){

//切片的拷贝操作
//切片使用copy内置函数完成拷贝,举例说明
var sliceone []int = []int{1,2,3,4,5}
var slicetwo = make([]int,10)
copy(slicetwo,sliceone)
fmt.Println("sliceone=",sliceone)//1 2 3 4 5
fmt.Println("slicetwo=",slicetwo)//1 2 3 4 5 0 0 0 0 0



}

6.copy的注意事项

思考题,下面代码有没有错误

1
2
3
4
5
var a[]int = []int{1,2,3,4,5}
var slice = make([]int,1)
fmt.Println(slice)//[0]
copy(slice,a)
fmt.Println(slice)//[1]

首先 切片 a长度已经明确是5,很多人认为进行copy(slice,a)时 因为slice大小长度为1

会不会因为长度问题而报错呢? 其实不是的,slice以自身的长度1,来存储copy过来的 a[0]的元素,仅存一个,其余四位不进行copy,是这个原理。

7.string与切片

string底层是一个byte数组,因此string也可以进行切片处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
import (
"fmt"
)

func main() {

//string底层是一个byte数组,因此string也可以进行切片处理
str := "hello@atguigu"
//使用切片获取到 atguigu
slice := str[6:]
fmt.Println("slice=", slice)

//string是不可变的,也就说不能通过 str[0] = 'z' 方式来修改字符串
//str[0] = 'z' [编译不会通过,报错,原因是string是不可变]

//如果需要修改字符串,可以先将string -> []byte / 或者 []rune -> 修改 -> 重写转成string
// "hello@atguigu" =>改成 "zello@atguigu"
// arr1 := []byte(str)
// arr1[0] = 'z'
// str = string(arr1)
// fmt.Println("str=", str)

// 细节,我们转成[]byte后,可以处理英文和数字,但是不能处理中文
// 原因是 []byte 字节来处理 ,而一个汉字,是3个字节,因此就会出现乱码
// 解决方法是 将 string 转成 []rune 即可, 因为 []rune是按字符处理,兼容汉字

arr1 := []rune(str)
arr1[0] = '北'
str = string(arr1)
fmt.Println("str=", str)
}

8.排序和查找

排序是将一组数据,依指定的顺序进行排列的过程。

排序的分类:

1)内部查找:

指将需要处理的所有数据都加载到内部存储器中进行排序。

2)外部排序:

数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包含(合并排序法和直接合并排序法)

冒泡排序(Bubble Sorting)的基本意思是:通过对待排序序列从后向前(从下表较大的元素开始),一次比较相邻的排序码,若发现里逆序则交换,使排序码较小的元素逐渐从后补移向前部(从下表比较大的单元移向下表比较小的单元),就像水底的气泡一样逐渐的向上冒。

因为排序的过程中各元素不断接近自己的位置,如果一趟比较下来没有进行交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行交换。

从而减少不必要的比较(优化)。

下图演示了一个冒泡的过程例子:

冒泡排序实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main
import (
"fmt"
)

//冒泡排序
func BubbleSort(arr *[5]int) {

fmt.Println("排序前arr=", (*arr))
temp := 0 //临时变量(用于做交换)


//冒泡排序..一步一步推导出来的
for i :=0; i < len(*arr) - 1; i++ {

for j := 0; j < len(*arr) - 1 - i; j++ {
if (*arr)[j] > (*arr)[j + 1] {
//交换
temp = (*arr)[j]
(*arr)[j] = (*arr)[j + 1]
(*arr)[j + 1] = temp
}
}

}


fmt.Println("排序后arr=", (*arr))

}

func main() {

//定义数组
arr := [5]int{24,69,80,57,13}
//将数组传递给一个函数,完成排序

BubbleSort(&arr)

fmt.Println("main arr=", arr) //有序? 是有序的
}