map
(⼀)、什么是map
1、map是Go中的内置类型,它将⼀个值与⼀个键关联起来。可以使⽤相应的键
检索值。
有资料翻译成地图、映射或字典。但是⼤多数习惯上翻译成集合。
map 是⼀种⽆序的键值对(key-value pair )的集合。map通过 key 来快
速检索数据,key 类似于索引,指向相应的value值。
map 是⼀种集合,所以可以像遍历数组或切⽚那样去遍历它,不过map
是⽆序的,所以⽆法决定它的展示顺序,这是因为 map 是使⽤ hash 表来实
现的。
(⼆)、使⽤map的注意细节:
map是⽆序的,每次打印出来的map都会不⼀样,它不能通过index获
取,⽽必须通过key获取;
map的⻓度是不固定的,和slice⼀样可以扩展。内置的len()函数同样适⽤
于map,返回map拥有的键值对的数量。但是map不能通过cap()函数计算容
量(或者说cap()函数的参数不可以是map);
同⼀个map中key必须保证唯⼀。key的数据类型必须是可参与⽐较运算的
类型,也就是⽀持==或!=操作的类型。如布尔型、整数型、浮点型、字符串
型、数组。对于切⽚、函数等引⽤类型则不能作为键的类型;(Invalid map
key type: must not be must not be a function , map or slice)
map的value可以是任何数据类型。
和slice⼀样,map也是⼀种引⽤类型;
(三)、 map的⽤法
1、map的声明
可以使⽤var map 关键字来定义 map,也可以使⽤内建函数 make 。
(1)、使⽤map关键字定义map
var 变量名 map[key类型]value类型
var声明变量,默认 map 是 nil 。nil map 不能⽤来存放键值对。
var声明后,要么声明时初始化,要么再使⽤make()函数分配到内存空
间,这样才能在其中存放键值对。
(2)、使⽤ make 函数
变量名 := make(map[key类型]value类型)
该声明⽅式,如果不初始化 map,那么map也不等于nil。
2、往map中存放键值对(key-value pair )
(1)、声明时初始化数值
示例代码:
var country = map[string]string{
"China": "Beijing",
"France": "Paris",
"Italy": "Rome",
"Japan": "Tokyo",
//"Japan": "Tokyo2",//key名不能重复
}
示例代码:
map1 := map[string]string{
"element": "div",
"width": "100px",
"height": "200px",
"border": "solid",
"color": "red",
"background": "none",
}
示例代码:
rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5,
"C++":2 }
(2)、先声明再插⼊数值。
示例代码:
//1、定义时初始化
rating := map[string]float32{"C": 5, "Go": 4.5, "Python": 4.5,
"C++": 2}
fmt.Println(rating)
var country = map[string]string{
"China": "Beijing",
"France": "Paris",
"Italy": "Rome",
"Japan": "Tokyo",
//"Japan": "Tokyo2",//key名不能重复
}
fmt.Println(country)
//2、创建集合后再赋值
countryCapitalMap := make(map[string]string)
/* map 插⼊ key-value 对,各个国家对应的⾸都 */
countryCapitalMap["China"] = "Beijing"
countryCapitalMap["France"] = "Paris"
countryCapitalMap["Italy"] = "Rome"
countryCapitalMap["Japan"] = "Tokyo"
countryCapitalMap["India"] = "New Delhi"
3、map数值遍历
示例代码:
(1)、遍历key与value
for k, v := range countryCapitalMap {
fmt.Println("国家是:", k, "⾸都:", v)
}
(2)、只遍历value
for _, v := range countryCapitalMap {
fmt.Println("国家是:?", "⾸都:", v)
}
(3)、只遍历key
for k := range countryCapitalMap {
fmt.Println("国家是:", k, "⾸都:", countryCapitalMap[k])
}
4、查看元素在集合中是否存在
我们可以通过key获取map中对应的value值。语法为:map[key]
但是当key如果不存在的时候,会得到该value值类型的默认值,⽐如
string类型得到空字符串,int类型得到0。但是程序不会报错。
所以可以通过 value, ok := map[key] ,获取key/value是否存在。ok是
bool型,如果 ok 是 true, 则该键值对存在,否则不存在。
示例代码:
captial, ok := countryCapitalMap["United States"]
/* 如果 ok 是 true, 则存在,否则不存在 */
if ok {
fmt.Println("⾸都是:", captial)
} else {
fmt.Println("该国家的⾸都没有列出!")
}
(四)、delete() 函数
delete(map, key) 函数⽤于删除集合的某个元素, 参数为 map 和其对应的
key。删除函数不返回任何值。
示例代码:
func main() {
//1、创建并初始化map
map1 := map[string]string{
"element": "div",
"width": "100px",
"height": "200px",
"border": "solid",
"color": "red",
"background": "none",
}
//2、根据key删除map中的元素
fmt.Println("删除前:", map1)
if _, ok := map1["background"]; ok {
delete(map1, "background")
}
fmt.Println("删除后:", map1)
//3、清空map
//map1 = map[string]string{}
map1 = make(map[string]string)
fmt.Println("清空后:", map1 , len(map1))
}
(五)、清空map中所有元素
Go语⾔没有为map提供任何清空所有元素的函数;
清空map的唯⼀办法是重新make⼀个新的map;
不⽤担⼼垃圾回收的效率,Go语⾔的垃圾回收⽐写⼀个清空函数更⾼
效。
(六)、map是引⽤类型的
与切⽚相似,map是引⽤类型。当将map分配给⼀个新变量时,它们都指
向相同的内部数据结构。因此,⼀个的变化会反映另⼀个。
示例代码:
func main() {
personSalary := map[string]int{
"Steven": 18000,
"Daniel": 5000,
"Josh": 20000,
}
fmt.Println("原始薪资:", personSalary)
newPersonSalary := personSalary
newPersonSalary["Daniel"] = 8000
fmt.Println("修改后newPersonSalary:", newPersonSalary)
fmt.Println("personSalary受影响情况:", personSalary)
}
运⾏结果:
原始薪资: map[Steven:18000 Daniel:5000 Josh:20000]
修改后newPersonSalary: map[Steven:18000 Daniel:8000 Josh:20000]
personSalary受影响情况: map[Steven:18000 Daniel:8000 Josh:20000]
list
(⼀)、概述
1、list是⼀种⾮连续存储的容器,由多个节点组成,节点通过⼀些变量记录彼此
之间的关系。list有多种实现⽅法,如单向链表、双向链表等。
2、假设A、B、C三个都有电话号码,如果A把号码告诉B,B把号码告诉C。这个
过程就建⽴了⼀个单向链表结构;
A->B->C
3、如果在单链表的基础上,再从C开始,将⾃⼰的号码给告诉⾃⼰号码的⼈,
这样就形成了双向链表结构。
A-><-B-><-C
4、Go语⾔中list的实现原理是双向链表。list能⾼效地进⾏任意位置的元素插⼊
和删除操作。
5、Golang的标准库提供了⾼级的数据结构List。具体在包container/list。
container/list包⾥主要有两个数据结构类型:“Element”、“List”;
Element类型代表双向链表中的⼀个元素,相当于C++⾥⾯的"iterator";
List代表⼀个双向链表。List零值为⼀个空的、可⽤的链表。
Element有Prev和Next⽅法⽤于得到前⼀个或者下⼀个Element,Element
可以直接调⽤Value属性;
list的⽤法,请查看go语⾔学习⽂
档:https://www.studygolang.com/pkgdoc
6、list包中两种类型:Element及List的核⼼⽅法
(1)、type Element
func (e *Element) Next() *Element
func (e *Element) Prev() *Element
(2)、type List
func New() *List
func (l *List) Init() *List
func (l *List) Len() int
func (l *List) Front() *Element
func (l *List) Back() *Element
func (l *List) PushFront(v interface{}) *Element
func (l *List) PushFrontList(other *List)
func (l *List) PushBack(v interface{}) *Element
func (l *List) PushBackList(other *List)
func (l *List) InsertBefore(v interface{}, mark *Element) *Element
func (l *List) InsertAfter(v interface{}, mark *Element) *Element
func (l *List) MoveToFront(e *Element)
func (l *List) MoveToBack(e *Element)
func (l *List) MoveBefore(e, mark *Element)
func (l *List) MoveAfter(e, mark *Element)
func (l *List) Remove(e *Element) interface{}
(⼆)、声明list
list的声明有两种⽅法:New和List声明。
1、通过container/list包的New⽅法声明list
变量名 := list.New()
2、通过var声明list
var 变量名 list.List
list与切⽚和map不同,没有具体元素类型的限制。list中的元素可以是任
意类型。
在CPP⾥⾯,list的成员必须是同⼀个数据类型,但是Go语⾔中却允许list
中插⼊任意类型的成员。
建议使⽤New()实现声明list。
(三)、element常⽤⽅法
1、func (e *Element) Next() *Element
Next返回链表的后⼀个元素或者nil。
2、func (e *Element) Prev() *Element
Prev返回链表的前⼀个元素或者nil。
(四)、list常⽤⽅法
1、func New() *List
New创建⼀个链表。
2、func (l *List) Init() *List
Init清空链表。
3、func (l *List) Len() int
Len返回链表中元素的个数,复杂度O(1)。
4、func (l *List) Front() *Element
Front返回链表第⼀个元素或nil。
5、func (l *List) Back() *Element
Back返回链表最后⼀个元素或nil。
6、func (l *List) PushFront(v interface{}) *Element
PushBack将⼀个值为v的新元素插⼊链表的第⼀个位置,返回⽣成的新元素。
7、func (l *List) PushFrontList(other *List)
添加另⼀个列表到开头。PushFrontList创建链表other的拷⻉,并将拷⻉的最后
⼀个位置连接到链表l的第⼀个位置。
8、func (l *List) PushBack(v interface{}) *Element
PushBack将⼀个值为v的新元素插⼊链表的最后⼀个位置,返回⽣成的新元素。
9、func (l *List) PushBackList(other *List)
追加另⼀个列表到末尾。PushBack创建链表other的拷⻉,并将链表l的最后⼀个
位置连接到拷⻉的第⼀个位置。
10、func (l *List) InsertBefore(v interface{}, mark *Element) *Element
InsertBefore将⼀个值为v的新元素插⼊到mark前⾯,并返回⽣成的新元素。如
果mark不是l的元素,l不会被修改。
11、func (l *List) InsertAfter(v interface{}, mark *Element) *Element
InsertAfter将⼀个值为v的新元素插⼊到mark后⾯,并返回新⽣成的元素。如果
mark不是l的元素,l不会被修改。
12、func (l *List) MoveToFront(e *Element)
MoveToFront将元素e移动到链表的第⼀个位置,如果e不是l的元素,l不会被修
改。
13、func (l *List) MoveToBack(e *Element)
MoveToBack将元素e移动到链表的最后⼀个位置,如果e不是l的元素,l不会被
修改。
14、func (l *List) MoveBefore(e, mark *Element)
MoveBefore将元素e移动到mark的前⾯。如果e或mark不是l的元素,或者
e==mark,l不会被修改。
15、func (l *List) MoveAfter(e, mark *Element)
MoveAfter将元素e移动到mark的后⾯。如果e或mark不是l的元素,或者
e==mark,l不会被修改。
16、func (l *List) Remove(e *Element) interface{}
Remove删除链表中的元素e,并返回e.Value。
(五)、遍历list
1、顺序遍历
for e := l.Front(); e != nil; e = e.Next() {
fmt.Print(e.Value, " ")
}
2、逆序遍历
for e := l.Back(); e != nil; e = e.Prev() {
fmt.Print(e.Value, " ")
}
(六)、list是值类型还是引⽤类型
1、list本质是什么?
type List struct {
root Element // sentinel list element, only &root, root.prev, and root.next
are used
len int // current list length excluding (this) sentinel element
}
type Element struct {
next, prev *Element
// The list to which this element belongs.
list *List
// The value stored with this element.
Value interface{}
}
2、struct是值类型
3、示例代码:
package main
import (
"container/list"
"fmt"
)
func main() {
copyList()
}
//list是值类型,不过采⽤New()⽅法声明的是⼀个指针。所以在拷⻉操作和
参数传递时具有引⽤类型的特征。
func copyList() {
//声明list1
list1 := list.New()
printListInfo2("刚声明的list1:", list1)
//给list1赋值
list1.PushBack("one")
list1.PushBack(2)
list1.PushBack("three")
list1.PushFront("first")
printListInfo2("赋值后的list1", list1)
iterateList2(list1)
//将list1拷⻉给list2。其实拷⻉的是地址
list2 := list1
printListInfo2("刚拷⻉的list2", list2)
iterateList2(list2)
//list2修改后
list2.PushBack(250)
list2.PushBack(350)
list2.PushBack(450)
printListInfo2("修改后的list2", list2)
iterateList2(list2)
//list2的修改是否影响到list1?
printListInfo2("修改list2的list1", list1)
iterateList2(list1)
}
func printListInfo2(info string, l *list.List) {
fmt.Println(info + "----------")
fmt.Printf("%T:%v \t , ⻓度为:%d \n", l, l, l.Len())
fmt.Println("----------")
}
func iterateList2(l *list.List) {
i := 0
for e := l.Front(); e != nil; e = e.Next() {
i++
fmt.Printf("%d:%v \t", i, e.Value)
}
fmt.Println("\n----------")
}
4、结论:
list是值类型,不过采⽤list的New()⽅法声明的是⼀个指针变量。所以在
拷⻉操作和参数传递时具有引⽤类型的特征。