柚子快報邀請碼778899分享:golang數(shù)據(jù)
柚子快報邀請碼778899分享:golang數(shù)據(jù)
5.數(shù)據(jù)
5.1字符串
關于各種編碼方式
Unicode字符
為多平臺下的字符和文本提供唯一的標識碼,是一個字符集
ASCll碼
Unicode是對ASCII的擴展,ASCII的128個字符在Unicode中保持了相同的代碼點。
UTF-8,UTF-16,UTF-32是Unicode的編碼方案
UTF-8:這是最常用的Unicode編碼方式。UTF-8是一種變長編碼,每個Unicode字符可能占用1到4個字節(jié)。
ASCII字符(U+0000到U+007F)只占用一個字節(jié),拉丁字母和常見的標點符號占用2個字節(jié),而其他較復雜的字符(例如很多亞洲字符比如漢字)則可能占用3個或4個字節(jié)
UTF-16:在UTF-16編碼中,字符占用2個或4個字節(jié)
UTF-32:在UTF-32編碼中,每個Unicode字符都表示為4個字節(jié)
func main() {
?
? ?
? ?// byte
for i := 0; i < len(s); i++ { ? ? ? ?
fmt.Printf("%d: %X\n", i, s[i])
}
? ?
? ?// rune
for i, c := range s {
fmt.Printf("%d: %U\n", i, c)
}
}
0: E9
1: 9B
2: A8
3: E7
4: 97
5: 95
?
0: U+96E8
3: U+75D5
*/
在golang中默認為UTF-8編碼,所以每個Unicode字符可能占用1到4個字節(jié)
在這里s中每個漢字占用3個字符,有較少使用的漢字占用四個字符
utf8.RuneCountInString(s)可以輸出字符的數(shù)量而不是字節(jié)長度
字符串的轉(zhuǎn)換
可在 rune、byte、string 間轉(zhuǎn)換。
func main() {
? ?var r rune = '我'
?
? ?var s ?string = string(r)
? ?var b ?byte ? = byte(r)
? ?var s2 string = string(b)
? ?var r2 rune ? = rune(b)
?
? ?fmt.Printf("%c, %U\n", r, r)
fmt.Printf("%c, %U\n", b, b)
?
? ?fmt.Printf("%s, %X, %X, %X\n", s, b, s2, r2)
?
}
?
// 我, U+6211
//, U+0011 從rune(int32)到byte(uint8)只保留低八位所以無法輸出正確結果
//所以字符串一般轉(zhuǎn)化為[]byte
// 我, 11, 11, 11
要修改字符串,須轉(zhuǎn)換為可變類型([]rune 或 []byte),待完成后再轉(zhuǎn)換回來。但不管如何轉(zhuǎn)換,都需重新分配內(nèi)存,并復制數(shù)據(jù)。
rune 到 string 的轉(zhuǎn)換是直接的,string(r) 會將 rune 值轉(zhuǎn)換為對應的 UTF-8 字符串。 rune 到 byte 的轉(zhuǎn)換,即 byte(r),只會保留 rune 的低8位,這可能會導致信息丟失,特別是對于多字節(jié)的 UTF-8 字符(如中文字符)。 byte 到 string 的轉(zhuǎn)換,string(b),會將單個 byte 轉(zhuǎn)換為字符串。 byte 到 rune 的轉(zhuǎn)換,rune(b),會將 byte 值視為一個 rune。但由于 byte 只能表示 0-255 的值,這在處理 Unicode 字符(尤其是中文)時會有局限性。
5.2數(shù)組
初始化方式
? ?b := [4]int{2, 5} ? ? ? ? ? ?// 未提供初始值的元素自動初始化為 0。
c := [4]int{5, 3: 10} ? ? ? ?// 可指定索引位置初始化。
d := [...]int{1, 2, 3} ? ? ? // 編譯器按初始化值數(shù)量確定數(shù)組長度。
e := [...]int{10, 3: 100} ? ?// 支持索引初始化,數(shù)組長度與此有關。
5.3切片
以指針引用底層數(shù)組片段,限定讀寫區(qū)域。
type slice struct {
array unsafe.Pointer
len ? int
cap ? int
}
引用類型,未初始化為nil
基于數(shù)組,初始化或者make函數(shù)創(chuàng)建
無法用==,!=操作只能判斷是否為nil
以索引訪問元素,可獲取底層
func main() {
a := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // array
? ?
s := a[2:6:8]
? ?//cap=8-2=6 len=6-2=4
fmt.Println(s)
fmt.Println(len(s), cap(s))
?
? ?// 引用原數(shù)組。
? ?
fmt.Printf("a: %p ~ %p\n", &a[0], &a[len(a) - 1])
fmt.Printf("s: %p ~ %p\n", &s[0], &s[len(s) - 1])
}
func main() {
a := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s := a[:]
?
? ?fmt.Println(s, len(s), cap(s))
}
?
/*
?
expr ? ? ? slice ? ? ? ? ? ? ? ? len ? cap ?
----------+-----------------------+----+-----+---------------
a[:] ? ? ? [0 1 2 3 4 5 6 7 8 9] ? 10 ? 10 ? a[0:len(a)]
a[2:5] ? ? [2 3 4] ? ? ? ? ? ? ? ? 3 ? ? 8
a[2:5:7] ? [2 3 4] ? ? ? ? ? ? ? ? 3 ? ? 5
a[4:] ? ? [4 5 6 7 8 9] ? ? ? ? ? 6 ? ? 6 ? a[4:len(a)]
a[:4] ? ? [0 1 2 3] ? ? ? ? ? ? ? 4 ? ? 10 ? a[0:4]
a[:4:6] ? [0 1 2 3] ? ? ? ? ? ? ? 4 ? ? 6 ? a[0:4:6]
?
*/
func main() {
? ?
? ?// 按初始化值,自動分配底層數(shù)組。
s1 := []int{ 0, 1, 2, 3 }
fmt.Println(s1, len(s1), cap(s1))
?
? ?// 自動創(chuàng)建底層數(shù)組。
s2 := make([]int, 5)
fmt.Println(s2, len(s2), cap(s2))
?
s3 := make([]int, 0, 5)
fmt.Println(s3, len(s3), cap(s3))
}
?
/*
?
s1: [0 1 2 3], ? len = 4, cap = 4
s2: [0 0 0 0 0], len = 5, cap = 5
s3: [], ? ? ? ? len = 0, cap = 5
?
*/
//golang中無法直接將slice轉(zhuǎn)變?yōu)閍rray,array可以轉(zhuǎn)變?yōu)閟lice
?
?
?
?
func main() {
s := NewStack()
? ?
// push
for i := 0; i < 5; i++ {
s.Push(i + 10)
}
? ?
// pop
for i := 0; i < 7; i++ {
fmt.Println(s.Pop())
}
}
5.4字典
引用類型,初始化或用make創(chuàng)建
主鍵支持== ,!=判斷
可判斷字典是否為nil,不支持比較操作
func main() {
m := map[string]int{}
?
// 是否有 { "a": 0 } 存在?
? ?
x := m["a"]
fmt.Println(x) ? ? ? // 0
?
x, ok := m["a"]
fmt.Println(x, ok) ? // 0 false
m["a"] = 0
x, ok = m["a"]
fmt.Println(x, ok) ? // 0 true
}
//建議以 ok-idiom 訪問主鍵,確認是否存在。
//刪除(delete)不存在鍵值,不會引發(fā)錯誤。清空(clear)字典,不會收縮內(nèi)存??蓪?nil 字典讀、刪除,但寫會引發(fā) panic
?
func main() {
? ?var m map[string]int ?// nil
_ = m["a"]
delete(m, "a")
?
// m["a"] = 0
// ~~~~~~ panic: assignment to entry in nil map
}
//清空(clear)字典,不會收縮內(nèi)存。
//Go語言的內(nèi)存管理是基于垃圾回收的。垃圾回收器會定期檢查不再引用的對象,并釋放它們所占用的內(nèi)存
?
func main() {
m := make(map[int]struct{})
?
for i := 0; i < 10; i++ {
m[i] = struct{}{}
}
?
for i := 0; i < 4; i++ {
for k, _ := range m {
print(k, ",")
}
?
println()
}
}
?
/*
?
7,2,3,4,5,9,0,1,6,8,
1,6,8,9,0,3,4,5,7,2,
0,1,6,8,9,2,3,4,5,7,
9,0,1,6,8,7,2,3,4,5,
?
*/
因 內(nèi)聯(lián)存儲、鍵值遷移 及 內(nèi)存安全 需要,字典被設計成不可尋址。不能直接修改值成員(結構或數(shù)組),應整體賦值,或以指針代替。
內(nèi)聯(lián)存儲 1.18中是128B
如果超出128B,則重新分配內(nèi)存并復制
5.5結構
結構體將多個字段序列打包為一個符合類型
? // 匿名結構
user := struct {
id ? int
name string
}{
id : 1,
name: "user1",
}
?
如果想比較兩個結構體是否相同,
全部字段支持。
內(nèi)存布局相同,但類型(如data1與data2)不同,不能比較。
布局相同(含字段名、標簽等)的匿名結構視為同一類型。
// 匿名結構類型相同的前提是
? ?// 字段名、字段類型、標簽和排列順序都相同。
? ?
d1 := struct {
x int
s string
}{ 100, "abc" }
?
var d2 struct {
x int
s string
}
?
d2.x = 100
d2.s = "abc"
?
println(d1 == d2) // true
空結構的一般用于值可以被忽略的場合。
標簽是結構的一部分
內(nèi)存布局相同時可以相互轉(zhuǎn)換
func main() {
?
type user struct {
id int `id`
}
?
type user2 struct {
id int `uid` ? ? ?// !!!!
}
?
u1 := user{1}
u2 := user2{2}
?
// 類型不同。
?
// _ = u1 == u2
// ? ? ~~~~~~~~ mismatched types user and user2
?
// 內(nèi)存布局相同,支持轉(zhuǎn)換。
u1 = user(u2)
fmt.Println(u1)
}
運行期,可以利用reflect獲取標簽信息,常用于格式校驗,數(shù)據(jù)庫關系映射
匿名字段
沒有名字只有類型
以類型名作為字段名,嵌入類型
type Attr struct {
perm int
}
?
type File struct {
Attr ? ? ? ? ? ?// Attr: Attr
name string
}
?
func main() {
f := File {
name: "test.dat",
?
// 必須顯式名稱初始化。
?
// perm: 0644,
// ~~~~ unknown field 'perm' in struct literal of type File
?
Attr: Attr{
perm: 0644,
},
}
?
// 像直屬字段訪問嵌入字段。
fmt.Printf("%s, %o\n", f.name, f.perm)
}
//隱式字段不含包名
type data struct {
os.File ? ? ? ? // File: os.File
}
?
func main() {
d := data{
File: os.File{},
}
?
fmt.Printf("%#v\n", d)
}
除 接口指針 和 多級指針 以外的任何 命名類型 都可作為匿名字段。
重名:
編譯器優(yōu)先選擇直屬命名字段,或按嵌入層次逐級查找匿名類型成員。
如匿名類型成員被外層同名遮蔽,那么必須用顯式字段名。
(其他情況可以使用隱式字段名
內(nèi)存
結構內(nèi)存一次分配,各字段相鄰地址空間按定義順序排列。(還記得字典是無法操作內(nèi)存的嗎
5.6指針
柚子快報邀請碼778899分享:golang數(shù)據(jù)
相關文章
本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權,聯(lián)系刪除。