1 字典
map是一種較為特殊的數(shù)據(jù)結(jié)構(gòu)穗慕,在任何一種編程語言中都可以看見他的身影享钞,它是一種鍵值對結(jié)構(gòu),通過給定的key可以快速獲得對應(yīng)的value栗竖。
1.1 如何定義字典
package main
import "fmt"
var m1 map[string]int//簡單可以放在函數(shù)外
func main() {
m2 := make(map[int]interface{}, 100)//必須函數(shù)內(nèi)
m3 := map[string]string{
"name": "james",
"age": "35",
}//必須函數(shù)內(nèi)
fmt.Println(m2[1])
fmt.Println(m3["name"])
fmt.Println("Hello")
}
在定義字典時不需要為其指定容量,因為map是可以動態(tài)增長的狐肢,但是在可以預(yù)知map容量的情況下,為了提高程序的效率也最好提前標明程序的容量份名。需要注意的是,不能使用不能比較的元素作為字典的key妓美,例如數(shù)組,切片等壶栋。而value可以是任意類型的辰如,如果使用interface{}作為value類型,那么就可以接受各種類型的值贵试,只不過在具體使用的時候需要使用類型斷言來判斷類型。
1.2 字典操作
向字典中放入元素也非常簡單
package main
import "fmt"
func main() {
m3 := map[string]string{
"name": "james",
"age": "35",
}
fmt.Println(m3["name"])
m3["key1"] = "v1"
m3["key2"] = "v2"
m3["key3"] = "v3"
fmt.Println(m3["key2"])
}
你可以動手試一下毙玻,如果插入的兩個元素key相同會發(fā)生什么桑滩?
與數(shù)組和切片一樣幌氮,我們可以使用len來獲取字典的長度戳吝。
package main
import "fmt"
func main() {
m3 := map[string]string{
"name": "james",
"age": "35",
}
m3["key1"] = "v1"
m3["key2"] = "v2"
m3["key3"] = "v3"
fmt.Println(len(m3))
}
在有些情況下浩销,我們不能確定鍵值對是否存在塘雳,或者當前value存儲的是否就是空值,go語言中我們可以通過下面這種方式很簡便的進行判斷普筹。
package main
import "fmt"
func main() {
m3 := map[string]string{
"name": "james",
"age": "35",
}
value0, ok0 := m3["name"]
fmt.Println(ok0)
fmt.Println("v=" + value0)
value1, ok1 := m3["n"]
fmt.Println(ok1)
fmt.Println("v=" + value1)
value2 := m3["age"]
fmt.Println("v=" + value2)
//if支持1個初始化語句, 初始化語句和判斷條件以分號分隔
if value, ok := m3["name"]; ok { //條件為真败明,指向{}語句
fmt.Println("if包含下:" + value)
}
}
上面這段代碼的作用就是如果當前字典中存在key為name的字符串則取出對應(yīng)的value,并返回true太防,否則返回false妻顶。
對于一個已經(jīng)存在的字典,我們?nèi)绾螌ζ溥M行遍歷呢蜒车?可以使用下面這種方式:
package main
import "fmt"
func main() {
m3 := map[string]string{
"name": "james",
"age": "35",
}
for key, value := range m3 {
fmt.Println("key: ", key, " value: ", value)
}
}
如果多運行幾次上面的這段程序會發(fā)現(xiàn)每次的輸出順序并不相同讳嘱,對于一個字典來說其默認是無序的,那么我們是否可以通過一些方式使其有序呢酿愧?你可以動手嘗試一下沥潭。(提示:可以通過切片來做哦)
如果已經(jīng)存在與字典中的值已經(jīng)沒有作用了,我們想將其刪除怎么辦呢嬉挡?可以使用go的內(nèi)置函數(shù)delete來實現(xiàn)钝鸽。
package main
import "fmt"
func main() {
m3 := map[string]string{
"name": "james",
"age": "35",
"key1": "----",
}
for key, value := range m3 {
fmt.Println("key: ", key, " value: ", value)
}
delete(m3, "key1")
_, ok := m3["key1"]
fmt.Println(ok)
}
除了上面的一些簡單操作,我們還可以聲明值類型為切片的字典以及字典類型的切片等等庞钢,你可以動手試試看拔恰。
不僅如此我們還可以將函數(shù)作為值類型存入到字典中。
package main
import "fmt"
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
}
m["devide"] = func(a, b int) int {
return a / b
}
fmt.Println(m["add"](3, 2))
fmt.Println(m["multi"](3, 2))
fmt.Println(m["devide"](3, 2))
}
2 字符串
字符串應(yīng)該可以說是所有編程語言中最為常用的一種數(shù)據(jù)類型基括,接下來我們就一起探索下go語言中對于字符串的常用操作方式颜懊。
2.1 字符串定義
字符串是一種值類型,在創(chuàng)建字符串之后其值是不可變的阱穗,也就是說下面這樣操作是不允許的饭冬。
package main
func main() {
s := "hello"
s[0] = 'T'
}
編譯器會提示cannot assign to s[0]。在C語言中字符串是通過\0來標識字符串的結(jié)束揪阶,而go語言中是通過長度來標識字符串是否結(jié)束的昌抠。
如果我們想要修改一個字符串的內(nèi)容,我們可以將其轉(zhuǎn)換為字節(jié)切片鲁僚,再將其轉(zhuǎn)換為字符串炊苫,但是也同樣需要重新分配內(nèi)存。
package main
import "fmt"
func main() {
s := "hello"
fmt.Println(s) //gello
b := []byte(s)
b[0] = 'g'
s1 := ""
s1 = string(b)
fmt.Println(s1) //gello
}
與其他數(shù)據(jù)類型一樣也可以通過len函數(shù)來獲取字符串長度冰沙。
package main
import "fmt"
func main() {
s := "hello"
fmt.Println(len(s)) //gello
}
但是如果字符串中包含中文就不能直接使用byte切片對其進行操作侨艾,go語言中我們可以通過這種方式
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "hello你好中國"
fmt.Println(len(s)) //17
fmt.Println(utf8.RuneCountInString(s)) //9
b := []byte(s)
fmt.Println(len(b))
for i := 0; i < len(b); i++ {
fmt.Printf("%c", b[i])
} //hello?? ?¥???-???
fmt.Println()
r := []rune(s)//和utf8.RuneCountInString同
fmt.Println(len(r))
for i := 0; i < len(r); i++ {
fmt.Printf("%c", r[i])
} //hello你好中國
}
在go語言中字符串都是以utf-8的編碼格式進行存儲的,所以每個中文占三個字節(jié)加上hello的5個字節(jié)所以長度為17拓挥,如果我們通過utf8.RuneCountInString函數(shù)獲得的包含中文的字符串長度則與我們的直覺相符合唠梨。而且由于中文對于每個單獨的字節(jié)來說是不可打印的,所以可以看到很多奇怪的輸出侥啤,但是將字符串轉(zhuǎn)為rune切片則沒有問題当叭。
2.2 strings包
strings包提供了許多操作字符串的函數(shù)茬故。在這里你可以看到都包含哪些函數(shù)https://golang.org/pkg/strings/。
下面演示幾個例子:
package main
import (
"fmt"
"strings"
)
func main() {
var str string = "This is an example of a string"
//判斷字符串是否以Th開頭
fmt.Printf("%t\n", strings.HasPrefix(str, "Th"))
//判斷字符串是否以aa結(jié)尾
fmt.Printf("%t\n", strings.HasSuffix(str, "aa"))
//判斷字符串是否包含an子串
fmt.Printf("%t\n", strings.Contains(str, "an"))
}
2.3 strconv包
strconv包實現(xiàn)了基本數(shù)據(jù)類型與字符串之間的轉(zhuǎn)換蚁鳖。在這里你可以看到都包含哪些函數(shù)https://golang.org/pkg/strconv/磺芭。
下面演示幾個例子:
package main
import (
"fmt"
"strconv"
)
func main() {
i, err := strconv.Atoi("-42") //將字符串轉(zhuǎn)為int類型
s := strconv.Itoa(-42) //將int類型轉(zhuǎn)為字符串
fmt.Println(i, err, s)
}
若轉(zhuǎn)換失敗則返回對應(yīng)的error值。
2.4 字符串拼接
2.4.1 Sprintf
除了以上的操作外醉箕,字符串拼接也是很常用的一種操作钾腺,在go語言中有多種方式可以實現(xiàn)字符串的拼接,但是每個方式的效率并不相同讥裤,下面就對這幾種方法進行對比放棒。(關(guān)于測試的內(nèi)容會放在后面的章節(jié)進行講解,這里大家只要知道這些拼接方式即可)
1.Sprintf
//BenchmarkSprintf_test.go
package main
import (
"fmt"
"testing"
)
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()
}
2.4.2 +拼接
BenchmarkStringAdd_test.go
package main
import (
"strconv"
"testing"
)
const numbers = 100
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()
}
2.4.3 bytes.Buffer
BenchmarkBytesBuf_test.go
package main
import (
"bytes"
"strconv"
"testing"
)
const numbers = 100
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()
}
2.4.4 strings.Builder拼接
BenchmarkStringBuilder_test.go
package main
import (
"strconv"
"strings"
"testing"
)
const numbers = 100
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()
}
2.4.5 對比
被認為是基準測試坞琴,通過 "go test" 命令哨查,加上 -bench flag 來執(zhí)行。多個基準測試按照順序運行剧辐。
$ go test -bench.
goos: windows
goarch: amd64
BenchmarkBytesBuf-12 1072903 1105 ns/op
BenchmarkSprintf-12 71346 16485 ns/op
BenchmarkStringAdd-12 209205 5777 ns/op
BenchmarkStringBuilder-12 1370254 879 ns/op
PASS
ok _/C_/Users/learn/go/GoHello 7.943s
可以看到通過strings.Builder拼接字符串是最高效的。