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)
	}

字符串方法:

  1. ASCII字符使用len()函数获取字符串所占的字节长度
  2. 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
  1. 字符串拼接符“+”,除了使用+进行拼接,也可以使用WriteString()
	str1 := "你好,"
	str2 := "golang"
	var stringBuilder bytes.Buffer
	//节省内存分配,提高处理效率
	stringBuilder.WriteString(str1)
	stringBuilder.WriteString(str2)
	fmt.Println(stringBuilder.String())
  1. 字符串的格式化:print:结果写到标准输出 Sprint:结果会以字符串形式返回
%c  单一字符
%T  动态类型
%v  本来值的输出
%+v 字段名+值打印
%d  十进制打印数字
%p  指针,十六进制
%f  浮点数
%b 二进制
%s string
  1. 字符串查找

strings.Index(): 正向搜索子字符串
strings.LastIndex():反向搜索子字符串

类型转换

在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明:

//类型 B 的值 = 类型 B(类型 A 的值)
valueOfTypeB = type B(valueOfTypeA)
  1. []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)
}
  1. 整数 与 字符串
// 字符串与其他类型的转换
// 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)
  1. 浮点数 与字符串

    // 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中的数据结构还在更新中,感谢阅读!

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

佳辰辰辰辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值