golang第七卷---go中的数据结构
分享一个go语言高级编程学习网站:Go语言高级编程
数组
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。
因为数组的长度是固定的,所以在Go语言中很少直接使用数组。
Go语言数组的声明:
var 数组变量名 [元素数量]Type
数组是定长的,不可更改,在编译阶段就决定了
数组的定义方式:
var a [3]int // 定义长度为 3 的 int 型数组, 元素全部为 0
var b = [...]int{1, 2, 3} // 定义长度为 3 的 int 型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2} // 定义长度为 3 的 int 型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为 6 的 int 型数组, 元素为 1, 2, 0, 0, 5, 6
Go 语言中数组是值语义。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如 C 语言的数组),而是一个完整的值。当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组。如果数组较大的话,数组的赋值也会有较大的开销。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。
关于函数间传递数组
var arr [10]int
func foo1(arr [10]int) { //每次拷贝整个数组
...
}
func foo2(arr *[10]int) { //传递指针,效率更高
...
}
字符串
一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本,字符串是 UTF-8 字符的一个序列。
字符串的定义:
var str string = "golang"
go语言从底层就支持UTF-8编码。
Go 语言字符串的底层结构在 reflect.StringHeader 中定义:
type StringHeader struct {
Data uintptr
Len int
}
字符串结构由两个信息组成:第一个是字符串指向的底层字节数组,第二个是字符串的字节的长度。字符串其实是一个结构体,因此字符串的赋值操作也就是 reflect.StringHeader 结构体的复制过程,并不会涉及底层字节数组的复制。在前面数组一节提到的 [2]string 字符串数组对应的底层结构和 [2]reflect.StringHeader 对应的底层结构是一样的,可以将字符串数组看作一个结构体数组。
字符串是一种值类型,且值不可变,即创建某个文本后将无法再次修改这个文本的内容。
当字符为 ASCII 码表上的字符时则占用 1 个字节
如果使用``反引号,会被原样进行赋值和输出
fmt.Println(`\t golang`) // \t golang
字符串是字节的定长数组,byte 和 rune 都是字符类型,若多个字符放在一起,就组成了字符串
遍历
对于字符串中unicode字符集使用for range进行遍历,ascii字符集可以使用for range或者for循环遍历
var str1 string = "hello"
var str2 string = "hello,golang"
// 遍历
for i :=0; i< len(str1); i++{
fmt.Printf("ascii: %c %d\n", str1[i], str1[i])
}
for _, s := range str1{
fmt.Printf("unicode: %c %d\n ", s, s)
}
// 中文只能用 for range
for _, s := range str2{
fmt.Printf("unicode: %c %d\n ", s, s)
}
字符串方法:
- ASCII字符使用len()函数获取字符串所占的字节长度
- Unicode字符串长度使用utf8.RuneCountInString()函数
str3 := "hello"
str4 := "你好"
fmt.Println(len(str3)) // 1个字母占1个字节 4
fmt.Println(len(str4)) // 1个中文占3个字节,go从底层支持utf8 6
fmt.Println(utf8.RuneCountInString(str4)) // 2
- 字符串拼接符“+”,除了使用+进行拼接,也可以使用WriteString()
str1 := "你好,"
str2 := "golang"
var stringBuilder bytes.Buffer
//节省内存分配,提高处理效率
stringBuilder.WriteString(str1)
stringBuilder.WriteString(str2)
fmt.Println(stringBuilder.String())
- 字符串的格式化:print:结果写到标准输出 Sprint:结果会以字符串形式返回
%c 单一字符
%T 动态类型
%v 本来值的输出
%+v 字段名+值打印
%d 十进制打印数字
%p 指针,十六进制
%f 浮点数
%b 二进制
%s string
- 字符串查找
strings.Index(): 正向搜索子字符串
strings.LastIndex():反向搜索子字符串
类型转换
在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明:
//类型 B 的值 = 类型 B(类型 A 的值)
valueOfTypeB = type B(valueOfTypeA)
- []byte和string可以通过强制类型转换
package main
import "fmt"
func main() {
s1 := "localhost:8080"
fmt.Println(s1)
// 强制类型转换 string to byte
strByte := []byte(s1)
// 下标修改
strByte[len(s1)-1] = '1'
fmt.Println(strByte)
// 强制类型转换 []byte to string
s2 := string(strByte)
fmt.Println(s2)
}
- 整数 与 字符串
// 字符串与其他类型的转换
// str 转 int
newStr1 := "1"
intValue, _ := strconv.Atoi(newStr1)
fmt.Printf("%T,%d\n", intValue, intValue) // int,1
// int 转 str
intValue2 := 1
strValue := strconv.Itoa(intValue2)
fmt.Printf("%T, %s\n", strValue, strValue)
- 浮点数 与字符串
// str 转 float
string3 := "3.1415926"
f,_ := strconv.ParseFloat(string3, 32)
fmt.Printf("%T, %f\n", f, f) // float64, 3.141593
//float 转 string
floatValue := 3.1415926
//4个参数,1:要转换的浮点数 2. 格式标记(b、e、E、f、g、G)
//3. 精度 4. 指定浮点类型(32:float32、64:float64)
// 格式标记:
// ‘b’ (-ddddp±ddd,二进制指数)
// ‘e’ (-d.dddde±dd,十进制指数)
// ‘E’ (-d.ddddE±dd,十进制指数)
// ‘f’ (-ddd.dddd,没有指数)
// ‘g’ (‘e’:大指数,‘f’:其它情况)
// ‘G’ (‘E’:大指数,‘f’:其它情况)
//
// 如果格式标记为 ‘e’,‘E’和’f’,则 prec 表示小数点后的数字位数
// 如果格式标记为 ‘g’,‘G’,则 prec 表示总的数字位数(整数部分+小数部分)
formatFloat := strconv.FormatFloat(floatValue, 'f', 2, 64)
fmt.Printf("%T,%s",formatFloat,formatFloat)
切片(slice)
切片就是一种简化版的动态数组(长度不固定)
切片的结构定义,reflect.SliceHeader:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
可以看出切片的开头部分和 Go 字符串是一样的,但是切片多了一个 Cap 成员表示切片指向的内存空间的最大容量(对应元素的个数,而不是字节数)
切片的定义方式:
var (
a []int // nil 切片, 和 nil 相等, 一般用来表示一个不存在的切片
b = []int{} // 空切片, 和 nil 不相等, 一般用来表示一个空的集合
c = []int{1, 2, 3} // 有 3 个元素的切片, len 和 cap 都为 3
d = c[:2] // 有 2 个元素的切片, len 为 2, cap 为 3
e = c[0:2:cap(c)] // 有 2 个元素的切片, len 为 2, cap 为 3
f = c[:0] // 有 0 个元素的切片, len 为 0, cap 为 3
g = make([]int, 3) // 有 3 个元素的切片, len 和 cap 都为 3
h = make([]int, 2, 3) // 有 2 个元素的切片, len 为 2, cap 为 3
i = make([]int, 0, 3) // 有 0 个元素的切片, len 为 0, cap 为 3
)
添加切片元素
内置的泛型函数 append 可以在切片的尾部追加 N 个元素:
var a []int
a = append(a, 1) // 追加 1 个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加 1 个切片, 切片需要解包
//还可以在切片的开头添加元素
var b = []int{1,2,3}
b = append([]int{0}, b...) // 在开头添加 1 个元素
b = append([]int{-3,-2,-1}, b...) // 在开头添加 1 个切片
删除切片元素
a = []int{1, 2, 3}
a = a[:len(a)-1] // 删除尾部 1 个元素
a = a[:len(a)-N] // 删除尾部 N 个元素
//删除开头的元素可以直接移动数据指针:
b = []int{1, 2, 3}
b = b[1:] // 删除开头 1 个元素
b = b[N:] // 删除开头 N 个元素
映射(map)
map 是一种无序的键值对的集合
map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,map 是无序的,我们无法决定它的返回顺序,这是因为 map 是使用 hash 表来实现的
创建和初始化
dict := make(map[string]int)
dict := map[string]string{"Red":"#da1337", "Orange":"#e95a22"}
切记不要使用new创建map,否则会得到一个空引用的指针
使用映射
colors := make(map[string]string) //创建一个空映射
colors["Red"] = "#da1337" //赋值
var colors map[string]string
colors["Red"] = "#da1337" //error
value, ok := colors["Blue"] //判断键是否存在
if ok {
fmt.Println(value)
}
value := colors["Blue"] //判断读取到的值是否空值
if value != "" {
fmt.Println(value)
}
遍历
map 的遍历过程使用 for range 循环完成
如下:
scene := make(map[string]int)
scene["cat"] = 66
scene["dog"] = 4
scene["pig"] = 960
for k, v := range scene {
fmt.Println(k, v)
}
map是无序的,不要期望 map 在遍历时返回某种期望顺序的结果
删除
使用 delete() 内建函数从 map 中删除一组键值对,delete() 函数的格式如下:
delete(map, 键)
Go语言中并没有为 map 提供任何清空所有元素的函数、方法,清空 map 的唯一办法就是重新 make 一个新的 map,不用担心垃圾回收的效率,Go语言中的并行垃圾回收效率比写一个清空函数要高效的多。
注意map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。
来自各个文章的总结,其他go中的数据结构还在更新中,感谢阅读!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!