目录
字典
map
是一种较为特殊的数据结构,在任何一种编程语言中都可以看见他的身影,一种无序的基于key-value
的数据结构,通过给定的key
可以快速获得对应的value
。Go
语言中的map
是引用类型,必须初始化才能使用。
map定义
Go
语言中 map
的定义语法如下
map[KeyType]ValueType
其中
KeyType
:表示键的类型。ValueType
:表示键对应的值的类型。
map
类型的变量默认初始值为nil
,需要使用make()
函数来分配内存。语法为:
make(map[KeyType]ValueType, [cap])
在定义字典时不需要为其指定容量,因为map
是可以动态增长的,但是在可以预知map
容量的情况下为了提高程序的效率也最好提前标明程序的容量。需要注意的是,不能使用不能比较的元素作为字典的key
,例如数组,切片等。而value
可以是任意类型的,如果使用interface{}
作为value
类型,那么就可以接受各种类型的值,只不过在具体使用的时候需要使用类型断言来判断类型。
map的基本使用
func main() {
m := make(map[string]int, 8)
m["key1"] = 90
m["key2"] = 100
fmt.Println(m)
fmt.Println(m["key2"])
fmt.Printf("type of a:%T\n", m)
}
map
也支持在声明的时候填充元素,例如:
func main() {
userInfo := map[string]string{
"username": "root",
"password": "123456",
}
fmt.Println(userInfo)
}
与数组和切片一样,我们可以使用len
来获取字典的长度。
len(m)
map遍历
Go
语言中使用for range
遍历map
。
func main() {
m := make(map[string]int)
m["key1"] = 90
m["key2"] = 100
m["key3"] = 60
for k, v := range m {
fmt.Println(k, v)
}
}
但只想遍历key
的时候,可以按下面的写法:
func main() {
m := make(map[string]int)
m["key1"] = 90
m["key2"] = 100
m["key3"] = 60
for k := range m {
fmt.Println(k)
}
}
注意: 遍历map
时的元素顺序与添加键值对的顺序无关。
判断某个键是否存在
在有些情况下,我们不能确定键值对是否存在,或者当前value
存储的是否就是空值,go
语言中我们可以通过下面这种方式很简便的进行判断。
func main() {
m := make(map[string]int)
m["key1"] = 90
m["key2"] = 100
// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
v, ok := m["key1"]
if ok {
fmt.Println(v)
} else {
fmt.Println("不存在")
}
}
也可以这样写:
if value, ok := m["key1"]; ok {
fmt.Println(value)
}
使用delete()函数删除键值对
使用delete()
内建函数从map
中删除一组键值对,delete()
函数的格式如下:
delete(map, key)
其中:
map
:表示要删除键值对的map
key
:表示要删除的键值对的键
例如:
func main(){
m := make(map[string]int)
m["key1"] = 90
m["key2"] = 100
m["key3"] = 60
delete(m, "key1")//将key1:100从map中删除
for k,v := range m {
fmt.Println(k, v)
}
}
元素为map类型的切片
func main() {
var mapSlice = make([]map[string]string, 3)
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println("after init")
// 对切片中的map元素进行初始化
mapSlice[0] = make(map[string]string, 10)
mapSlice[0]["username"] = "root"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "192.168.1.18"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
值为切片类型的map
func main() {
var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
函数作为值类型存入到字典中
func main() {
m := make(map[string]func(a, b int) int)
m["add"] = func(a, b int) int {
return a + b
}
m["multi"] = func(a, b int) int {
return a * b
}
fmt.Println(m["add"](3, 2))
fmt.Println(m["multi"](3, 2))
}
字符串
字符串定义
符串是一种值类型,在创建字符串之后其值是不可变的,也就是说下面这样操作是不允许的。
s := "hello"
s[0] = 'T' // error
编译器会提示cannot assign to s[0]
。在C
语言中字符串是通过\0
来标识字符串的结束,而go
语言中是通过长度来标识字符串是否结束的。
如果我们想要修改一个字符串的内容,我们可以将其转换为字节切片,再将其转换为字符串,但是也同样需要重新分配内存。
func main() {
s := "hello"
b := []byte(s)
b[0] = 'g'
s = string(b)
fmt.Println(s) // gello
}
与其他数据类型一样也可以通过len
函数来获取字符串长度。
len(s)
但是如果字符串中包含中文就不能直接使用byte
切片对其进行操作,go
语言中我们可以通过这种方式:
func main() {
s := "hello你好中国"
fmt.Println(len(s)) // 17
fmt.Println(utf8.RuneCountInString(s)) // 9
b := []byte(s)
for i := 0; i < len(b); i++ {
fmt.Printf("%c", b[i])
} // helloä½ å¥½ä¸å�½
fmt.Println()
r := []rune(s)
for i := 0; i < len(r); i++ {
fmt.Printf("%c", r[i])
} // hello你好中国
}
在go
语言中字符串都是以utf-8
的编码格式进行存储的,所以每个中文占三个字节加上hello
的5
个字节所以长度为17
,如果我们通过utf8.RuneCountInString
函数获得的包含中文的字符串长度则与我们的直觉相符合。而且由于中文对于每个单独的字节来说是不可打印的,所以可以看到很多奇怪的输出,但是将字符串转为rune
切片则没有问题。
strings包
strings
包提供了许多操作字符串的函数。在这里你可以看到都包含哪些函数https://golang.org/pkg/strings/。
例如:
func main() {
var str string = "This is an example of a string"
//判断字符串是否以Th开头
fmt.Printf("%t\n", strings.HasPrefix(str, "Th"))
//判断字符串是否以aa结尾
fmt.Printf("%t\n", strings.HasSuffix(str, "aa"))
//判断字符串是否包含an子串
fmt.Printf("%t\n", strings.Contains(str, "an"))
}
strconv包
strconv
包实现了基本数据类型与字符串之间的转换。在这里你可以看到都包含哪些函数https://golang.org/pkg/strconv/。
func TestStringConv(t *testing.T) {
s := strconv.Itoa(10) // 将int类型转为字符串
fmt.Println(s)
if i, err := strconv.Atoi("10"); err != nil { // 将字符串转为int类型
fmt.Println(10 + i)
}
}
若转换失败则返回对应的error
值。
字符串拼接
除了以上的操作外,字符串拼接也是很常用的一种操作,在go
语言中有多种方式可以实现字符串的拼接,但是每个方式的效率并不相同。
SPrintf
const numbers = 100
func BenchmarkSprintf(b *testing.B) {
b.ResetTimer()
for idx := 0; idx < b.N; idx++ {
var s string
for i := 0; i < numbers; i++ {
s = fmt.Sprintf("%v%v", s, i)
}
}
b.StopTimer()
}
+拼接
func BenchmarkStringAdd(b *testing.B) {
b.ResetTimer()
for idx := 0; idx < b.N; idx++ {
var s string
for i := 0; i < numbers; i++ {
s += strconv.Itoa(i)
}
}
b.StopTimer()
}
bytes.Buffer
func BenchmarkBytesBuf(b *testing.B) {
b.ResetTimer()
for idx := 0; idx < b.N; idx++ {
var buf bytes.Buffer
for i := 0; i < numbers; i++ {
buf.WriteString(strconv.Itoa(i))
}
_ = buf.String()
}
b.StopTimer()
}
strings.Builder拼接
func BenchmarkStringBuilder(b *testing.B) {
b.ResetTimer()
for idx := 0; idx < b.N; idx++ {
var builder strings.Builder
for i := 0; i < numbers; i++ {
builder.WriteString(strconv.Itoa(i))
}
_ = builder.String()
}
b.StopTimer()
}