Go语言学习——4、数据存储:数组,切片,映射

目录

一、数组

1、声明数组

2、初始化数组

 3、遍历数组

二、切片

1、从数组或切片生成新的切片的语法格式:

2、直接生成一个新的切片

3、切片添加元素

4、从切片删除元素

5、遍历切片

三、映射

1、 声明映射

2、   初始化映射

3、    遍历映射

4、    从映射中删除键值对

四、拓展:并发操作映射(map)

1、map在单线程中不会出现错误,但多个线程并发访问一个map,会导致程序异常退出。

2、用sync包对map进行加锁,可以实现map多线程编程。

3、由于加锁会对程序产生一定的影响,所以使用sync.map进行map的安全开发


一、数组

        数组是具有相同类型且长度固定的一组数据项序列,数组中存放的数据类型可以是整形、字符串或其他数据类型。 使用数组前需要先声明数组,声明是必须指定数组的大小且指定后不能改变,数组元素可以通过下标来读取或修改,数组下标是从零开始的。

1、声明数组

数组声明语法格式如下:

    var 数组变量名 [数组长度]元素类型

//例如:声明数组student,长度为3,元素类型为string
    var student [3]string

只声明数组,没有对数组进行赋值,打印时为空数组。 

func example01() {
	fmt.Println("example01()..........")
	var student [3]string
	fmt.Println("student数组:",student)
	fmt.Println("----------------------\n")
}

/*
运行结果如下:
    example01()..........
    student数组: [  ]
    ----------------------
*/

2、初始化数组

初始化数组:数组可在声明时进行赋值,如下:

var student = [3]string{"Tom","Mike","Peter"}

         使用这种方式初始化数组,需要保证大括号中元素数量与数组大小一致。忽略中括号里的数字,不设置数组大小,可以通过"..."来代替数组大小。例如:

var student = [...]string{"Tom","Mike","Peter"}

数组初始化的举例:

func example02() {
	fmt.Println("example02()..........")
	var student = [...]string{"Tom", "Mike", "Peter"}
	fmt.Println("student数组:",student)
	fmt.Println("----------------------\n")
}

/*
运行结果如下:
    example02()..........
    student数组: [Tom Mike Peter]
    ----------------------
*/

 3、遍历数组

遍历数组:数组元素可以通过数组下标来读取或修改,数组下标从0开始,第一个元素的数组下标为0,第二个数组元素的下标为1,通过for循环进行遍历。

func example04() {
	fmt.Println("example04()..........")
	var student = [...]string{"Tom","Mike","Peter"}
	for k,v :=range student{
		fmt.Println("数组下标:",k,"\t对应元素:",v)
	}
	fmt.Println("---------------------")
}

/*
运行结果如下:
    example04()..........
    数组下标: 0 	对应元素: Tom
    数组下标: 1 	对应元素: Mike
    数组下标: 2 	对应元素: Peter
    ---------------------
*/

二、切片

        相对于数组,切片(slice)是一种更加强的数据结构,是多个同类型元素的连续集合,但是切片本身不存储任何元素,只是对现有数组的引用。

        切片的结构包括:地址,长度和容量

                (1)、 地址:切片的地址一般指切片中第一个元素指向的内存地址,十六进制表示。

                (2)、 长度:切片中实际存在的元素个数。

                (3)、容量:从切片起始元素开始到底层引用数组的最后一个元素的个数。

        切片的长度和容量都是可变的,通过追加元素使切片的长度和容量增大

        切片的生成方式由三种:

                 (1)、从数组生成一个新的切片。

                 (2)、从切片生成一个新的切片。

                 (3)、直接生成一个新的切片。

        切片的长度用len()函数获取 ,切片的容量用cap()函数获取。

1、从数组或切片生成新的切片的语法格式:

 var slice = 数组/切片[开始位置:结束位置]

从数组或切片新生成的切片有如下特性:

        (1)、新生成切片的长度:结束位置-开始位置。

        (2)、新生成切片取出的元素不包括结束位置对应的元素。

        (3)、新生成的切片是对原有数组或切片的引用,其地址与截取的数组或切片开始位置对应的元素地址相同。

        (4)、新生成切片的容量指从切片起始元素到其底层数组或切片中的最后一个元素的个数。

举例:

func example01() {
	fmt.Println("example01()...........") 
	var student = [...]string{"John", "Tom", "Mike", "Peter", "Ben", "Jack"}
	var student1 = student[1:5]
	var student2 = student1[0:2]
	fmt.Println("student数组:", student)               
	fmt.Println("student数组第1个元素的地址是:", &student[0]) 
	fmt.Println("student数组第2个元素的地址是:", &student[1]) 
	fmt.Println("student数组的长度是:", len(student))    
	fmt.Println("student数组的容量是:", cap(student))     

	//从数组生成新的切片
	fmt.Println("\nstudent1切片:", student1)        
	fmt.Println("student1数组的地址是:", &student1[0])  
	fmt.Println("student1切片的长度是:", len(student1)) 
	fmt.Println("student1切片的容量是:", cap(student1)) 

	//从切片生成新的切片
	fmt.Println("\nstudent1切片:", student2[:])     
	fmt.Println("student2数组的地址是:", &student2[0])  
	fmt.Println("student2切片的长度是:", len(student2)) 
	fmt.Println("student2切片的容量是:", cap(student2)) 
	fmt.Println("----------------------")
}

/*
运行结果如下:
    example01()...........
    student数组: [John Tom Mike Peter Ben Jack]
    student数组第1个元素的地址是: 0xc000048060
    student数组第2个元素的地址是: 0xc000048070
    student数组的长度是: 6
    student数组的容量是: 6

    student1切片: [Tom Mike Peter Ben]
    student1数组的地址是: 0xc000048070
    student1切片的长度是: 4
    student1切片的容量是: 5

    student1切片: [Tom Mike]
    student2数组的地址是: 0xc000048070
    student2切片的长度是: 2
    student2切片的容量是: 5
    ----------------------

2、直接生成一个新的切片

        切片的声明格式如下:

var 切片变量名 []元素类型

        切片声明后其内容为空,长度、容量都为0。需要对切片进行初始化,初始化切片的两种语法格式如下:

        (1)、在声明时初始化:

 var slice = []元素类型{value1,value2,value3}

        (2)、使用make()函数初始化:

var slice = make([]元素类型,切片长度,切片容量)

注意:初始化后会自动填充默认值。切片的容量必须大于等于切片的长度,否则程序会报错.

举例:

func example02() {
	fmt.Println("\n\nexample02()...........")
	var student []int
	fmt.Println("student切片:", student) 
	fmt.Println("student切片长度:", len(student)) 
	fmt.Println("student切片容量:", cap(student))  
	fmt.Println("student切片是否为空:", student == nil)

	//声明变量时初始化
	var student1 = []int{1, 2, 3, 4, 5}
	fmt.Println("\nstudent1切片:", student1)  
	fmt.Println("student1切片长度:", len(student1)) 
	fmt.Println("student1切片容量:", cap(student1)) 
	fmt.Println("student1切片是否为空:", student1 == nil)  

	//使用make()函数初始化
	var student2 = make([]int,4,5)
	fmt.Println("\nstudent2切片:", student2) 
	fmt.Println("student2切片长度:", len(student2)) 
	fmt.Println("student2切片容量:", cap(student2)) 
	fmt.Println("student2切片是否为空:", student2 == nil)  
	fmt.Println("----------------------")
}

/*
运行结果如下:
    example02()...........
    student切片: []
    student切片长度: 0
    student切片容量: 0
    student切片是否为空: true

    student1切片: [1 2 3 4 5]
    student1切片长度: 5
    student1切片容量: 5
    student1切片是否为空: false

    student2切片: [0 0 0 0]
    student2切片长度: 4
    student2切片容量: 5
    student2切片是否为空: false
    ----------------------

3、切片添加元素

         使用append()函数来对切片进行元素的添加。当切片不能再容纳其他元素时(即当前切片长度等于容量值)。 下一次使用append()函数添加元素,切片的容量会自动扩容两倍。

举例:

func example03() {
	fmt.Println("\n\nexample03()..............")
	student := make([]int,1,1)
	for i:=0;i<=8;i++ {
		student = append(student,i)
		fmt.Println("i的值为:",i,"当前切片的长度:",len(student),"当前切片容量:",cap(student))
	}
	fmt.Println(student)    
	fmt.Println("-----------------------------")
}

/*
运行结果如下:
    example03()..............
    i的值为: 0 当前切片的长度: 2 当前切片容量: 2
    i的值为: 1 当前切片的长度: 3 当前切片容量: 4
    i的值为: 2 当前切片的长度: 4 当前切片容量: 4
    i的值为: 3 当前切片的长度: 5 当前切片容量: 8
    i的值为: 4 当前切片的长度: 6 当前切片容量: 8
    i的值为: 5 当前切片的长度: 7 当前切片容量: 8
    i的值为: 6 当前切片的长度: 8 当前切片容量: 8
    i的值为: 7 当前切片的长度: 9 当前切片容量: 16
    i的值为: 8 当前切片的长度: 10 当前切片容量: 16
    [0 0 1 2 3 4 5 6 7 8]

如果切片是从其他数组或切片生成的。新切片的元素添加会覆盖对原有数组或切片中的元素。

举例:

func example04() {
	fmt.Println("\n\nexample04().............")
	var student = []string{"John", "Mike", "Ben", "Tom", "Jack"}
	var student1 = student[0:1]
	fmt.Println("student数组:",student)  
	fmt.Println("student1切片:",student1)   
	student1 = append(student1, "Peter")
	fmt.Println("student数组:",student)    
	fmt.Println("student1切片:",student1)  
	fmt.Println("----------------------------")
}

/*
运行结果如下:
    example04().............
    student数组: [John Mike Ben Tom Jack]
    student1切片: [John]
    student数组: [John Peter Ben Tom Jack]
    student1切片: [John Peter]
    ----------------------------

4、从切片删除元素

        Go语言没有为删除切片元素提供方法,我们需要手动的将删除点前后元素连接在一起,实现切片元素的删除。

        要清空切片中的所有元素,可以把开始下标和结束下标设置为0

举例:

func example05() {
	fmt.Println("\n\nexample05().................")
	var student = []string{"John", "Mike", "Ben", "Tom", "Jack"}
	fmt.Println("原student切片:",student)

	//删除元素
	student = append(student[0:1],student[3:]...)
	fmt.Println("student切片:",student)
	fmt.Println("student切片长度:", len(student))
	fmt.Println("student切片容量:", cap(student))

	//清空切片中的元素
	student = student[0:0]
	fmt.Println("student切片:",student)
	fmt.Println("student切片长度:", len(student))
	fmt.Println("student切片容量:", cap(student))
	fmt.Println("--------------------------------")
}

/*
运行结果如下:
    example05().................
    原student切片: [John Mike Ben Tom Jack]
    student切片: [John Tom Jack]
    student切片长度: 3
    student切片容量: 5
    student切片: []
    student切片长度: 0
    student切片容量: 5
--------------------------------
*/

5、遍历切片

        切片的遍历与数组相似,可以通过切片下标来遍历。切片下标从零开始。

举例:

func example06() {
	fmt.Println("\n\nexample06().................")
	var student = []string{"John", "Mike", "Ben", "Tom", "Jack"}
	for k,v := range student{
		fmt.Println("切片下标:",k,"对应切片元素:",v)
	}
	fmt.Println("---------------------------------")
}

/*
运行结果如下:
    example06().................
    切片下标: 0 对应切片元素: John
    切片下标: 1 对应切片元素: Mike
    切片下标: 2 对应切片元素: Ben
    切片下标: 3 对应切片元素: Tom
    切片下标: 4 对应切片元素: Jack
    ---------------------------------
*/

三、映射

映射(map):是一种无序的键值对的集合,map的键类似于索引,指向数据的值,当程序中需要存放有关联关系的数据,就会使用map

1、 声明映射

        语法格式如下:

var map = map[键类型]值类型{}

举例:

func example01() {
	fmt.Println("example01()..........")
	var studentMap = map[string]int{}
	fmt.Println(studentMap)
	fmt.Println("--------------------")
}

/*
运行结果如下:
    example01()..........
    map[]
    --------------------
*/

2、   初始化映射

        方式有两种

        (1)、声明时进行初始化

        (2)、使用make()函数进行初始化

举例:

func example02() {
	fmt.Println("\n\nexample02()............")
	//声明时初始化
	var studentMap = map[string]int{
		"Tom":  90,
		"John": 43,
		"Jack": 99,
	}
	fmt.Println(studentMap)

	//使用make()函数进行初始化
	var student map[string]int
	student = make(map[string]int)
	student["John"] = 90
	student["Tom"] = 30
	student["Ken"] = 80
	fmt.Println(student)
	fmt.Println("-----------------------")
}

/*
运行结果如下:
    example02()............
    map[Jack:99 John:43 Tom:90]
    map[John:90 Ken:80 Tom:30]
    -----------------------
*/

3、    遍历映射

        通过for循环遍历,遍历时可同时获取键和值,也可只获取键,只获取值。

举例:

func example03() {
	fmt.Println("\n\nexample03().........")
	var studentMap = map[string]int{
		"Tom":  90,
		"John": 43,
		"Jack": 99,
	}

	//遍历键和值
	for k, v := range studentMap {
		fmt.Println(k, v)
	}

	//只遍历键
	for k := range studentMap{
		fmt.Println(k)
	}

	//只遍历值
	for _ , v := range studentMap{
		fmt.Println(v)
	}
	fmt.Println("-------------------------")
}

/*
运行结果如下:
    example03().........
    Tom 90
    John 43
    Jack 99
    Tom
    John
    Jack
    90
    43
    99
-------------------------

4、    从映射中删除键值对

        使用delete()函数进行删除操作。语法格式如下:

delete(map,键)

举例:

func example04() {
	fmt.Println("\n\nexample04().........")
	var studentMap = map[string]int{
		"Tom":  90,
		"John": 43,
		"Jack": 99,
	}
	fmt.Println("studentMap:",studentMap)
	delete(studentMap,"Tom")
	fmt.Println("studentMap:",studentMap)
	fmt.Println("------------------------")
}

/*
运行结果如下:
    example04().........
    studentMap: map[Jack:99 John:43 Tom:90]
    studentMap: map[Jack:99 John:43]
    ------------------------
*/

注意:Go语言没有提供map清空元素的方法,想要清空map就需要重新定义一个新的map

四、拓展:并发操作映射(map)

1、map在单线程中不会出现错误,但多个线程并发访问一个map,会导致程序异常退出。

举例:

func exampleB01() {
	var GoMap = map[int]int{}
	for i := 0; i < 10000; i++ {
		go writeMapB01(GoMap, i, i)
		go readMapB01(GoMap, i)
	}
	fmt.Println("B01 Over")
}

func readMapB01(GoMap map[int]int, key int) int {
	return GoMap[key]
}

func writeMapB01(GoMap map[int]int, key int, value int) {
	GoMap[key] = value
}


/*
运行结果如下:
    fatal error: concurrent map writes

    goroutine 8 [running]:
    runtime.throw(0x4d0744, 0x15)
	    E:/Go/src/runtime/panic.go:1116 +0x79 fp=0xc000043f58 sp=0xc000043f28 pc=0x432889
    runtime.mapassign_fast64(0x4b3c40, 0xc00006e330, 0x1, 0x0)
	    E:/Go/src/runtime/map_fast64.go:101 +0x32a fp=0xc000043f98 sp=0xc000043f58 pc=0x41047a
main.writeMapB01(0xc00006e330, 0x1, 0x1)
	    E:/Work/GO/src/Go语言入门/Middle/B_Go语言内置容器/C_映射/Example_B01.go:61 +0x48 fp=0xc000043fc8 sp=0xc000043f98 pc=0x49e418
    runtime.goexit()
	    E:/Go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc000043fd0 sp=0xc000043fc8 pc=0x45d241
created by main.exampleB01
	    E:/Work/GO/src/Go语言入门/Middle/B_Go语言内置容器/C_映射/Example_B01.go:50 +0x66

2、用sync包对map进行加锁,可以实现map多线程编程。

举例:

var lock sync.RWMutex

func exampleB02() {
	var GoMap = map[int]int{}
	for i := 0; i < 10000; i++ {
		go writeMapB02(GoMap, i, i)
		go readMapB02(GoMap, i)
	}
	fmt.Println("B02 Over")
}

func readMapB02(GoMap map[int]int, key int) int {
	lock.Lock() //读取map前先加锁
	m := GoMap[key]
	lock.Unlock() //读完后解锁
	return m
}

func writeMapB02(GoMap map[int]int, key int, value int) {
	lock.Lock() //写map前加锁
	GoMap[key] = value
	lock.Unlock() //写完后解锁
}

/*
运行结果如下:
    B02 Over
*/

3、由于加锁会对程序产生一定的影响,所以使用sync.map进行map的安全开发

         sync.map有以下特点:

                (1)、使用前无需初始化,直接声明即可。无需使用make()函数创建。

                (2)、Load()方法的第一个返回值是接口类型的。需要将其转换为map值的类型。

举例:

func exampleB03() {
	var GoMap sync.Map
	for i := 0; i < 1000; i++ {
		go writeMapB03(&GoMap,i,i)
		go readMapB03(&GoMap,i)
	}
	fmt.Println("B03 Over")
}

func readMapB03(GoMap *sync.Map, key int) int{
	res , ok := GoMap.Load(key)
	if ok==true {
		return res.(int)
	}else {
		return 0
	}
}

func writeMapB03(GoMap *sync.Map, key int, value int) {
	GoMap.Store(key,value)
}

/*
运行结果如下:
    B03 Over
*/

你可能感兴趣的:(go语言,go语言)