Array
the basics of using array
package main
import "fmt"
func main() {
var x [3]int
x[0] = 2
fmt.Println(x)
}
输出
[2 0 0]
package main
import "fmt"
func main() {
var x = [3]int{4, 5, 6}
fmt.Println(x)
}
输出
[4 5 6]
Array ShortComing
Go considers the size of the array to be part of the type of the array.
This makes an array that’s declared to be[3]int
a different type from an array that’s declared to be [4]int
This also means that you cannot use a variable to specify the size of an array, because types must be resolved at compile time, not at runtime.
What’s more, you can’t use a type conversion to convert arrays of different sizes to identical types.
Because you can’t convert arrays of different sizes into each other, you can’t write a function that works with arrays of any size and you can’t assign arrays of different sizes to the same variable.
Slice
Using […]
makes an array. Using[]
makes a slice.
the basics of using slice
package main
import "fmt"
func main() {
var x = []int{10, 20, 30}
fmt.Println(x) // 10,20,30
x[1] = 25
fmt.Println(x) // 10 25 30
fmt.Println(x[1]) // 25
}
package main
import "fmt"
func main() {
var x []int
fmt.Println(x == nil) // true
}
the zero value for a slice is nil
var x []int
This creates a slice of int s. Since no value is assigned, x is assigned the zero value for a slice, which is is something we haven’t seen before: nil.
We’ll talk more about nil in Chapter 6, but it is slightly different from the null that’s
found in other languages.
In Go, nil is an identifier that represents the lack of a value for some types.
Like the untyped numeric constants we saw in the previous chapter, nil has no type, so it can be assigned or compared against values of different types.
A nil slice contains nothing.
grow slices—append
package main
import "fmt"
func main() {
var x []int
y := append(x, 3)
fmt.Println(x == nil)
fmt.Println(y)
}
输出
true
[3]
package main
import "fmt"
func main() {
var x = []int{10, 20, 30}
var y = []int{11, 22, 33}
z := append(x, y...)
fmt.Println(x)
fmt.Println(y)
fmt.Println(z)
}
输出
[10 20 30]
[11 22 33]
[10 20 30 11 22 33]
每调用一次append,slice的len肯定会增加,但是capacity不一定会增加
capacity & len
capacity can be larger than the length
As we’ve seen, a slice is a sequence of values.
Each element in a slice is assigned to consecutive memory locations, which makes it quick to read or write these values.
Every slice has a capacity, which is the number of consecutive memory locations reserved.
This can be larger than the length.
Each time you append to a slice, one or more values is added to the end of the slice. Each value added increases the length by one. When the length reaches the capacity, there’s no more room to put values.
If you try to add additional values when the length equals the capacity, the append function uses the Go runtime to allocate a new slice with a larger capacity.
The values in the original slice are copied to the new slice, the new values are added to the end, and the new slice is returned.
func main() {
var x []int
fmt.Println(x, len(x), cap(x))
x = append(x, 10)
fmt.Println(x, len(x), cap(x))
x = append(x, 20)
fmt.Println(x, len(x), cap(x))
x = append(x, 30)
fmt.Println(x, len(x), cap(x))
x = append(x, 40)
fmt.Println(x, len(x), cap(x))
x = append(x, 50)
fmt.Println(x, len(x), cap(x))
}
输出
[] 0 0
[10] 1 1
[10 20] 2 2
[10 20 30] 3 4
[10 20 30 40] 4 4
[10 20 30 40 50] 5 8
每调用一次append,slice的len肯定会增加,但是capacity不一定会增加
make—create an empty slice
create an empty slice that already has a length or capacity specified.
That’s the job of the built-in make function.
It allows us to specify the type, length, and, optionally, the capacity
x := make([] int, 5 )
This creates an int slice with a length of 5 and a capacity of 5.
Since it has a length of 5, x[0] through x[4] are valid elements, and they are all initialized to 0
x := make([] int , 5 , 10 )
This creates an int slice with a length of 5 and a capacity of 10.
x := make([]int,0,10)
In this case, we have a non-nil slice with a length of zero, but a capacity of 10. Since the length is 0, we can’t directly index into it, but we can append values to it
append always increases the length of a slice.
x = append( x ,5 ,6 ,7 ,8 )
The value of x is now [5 6 7 8], with a length of 4 and a capacity of 10.
append always increases the length of a slice
Declaring Your Slice
create a nil slice
var data []int
create a slice using an empty slice literal.
// This creates a zero-length slice, which is non-nil
var x = []int{}
declaring a slice with default values
data := []int{2, 4, 6, 8}
slice expression & len & cap
It’s written inside of brackets and consists of a starting offset and an ending offset, separated by a colon (:)
.
If you leave off the starting offset, 0 is assumed. Likewise, if you leave off the ending offset, the end of the slice is substituted.
slice expression ,是属于左含右不含,即[)
slice number means index,start from zero
package main
import "fmt"
func main() {
x := []int{10, 20, 30, 40}
y := x[:2]
z := x[1:]
d := x[1:3]
e := x[:]
fmt.Println(x)
fmt.Println(y)
fmt.Println(z)
fmt.Println(d)
fmt.Println(e)
}
输出
[10 20 30 40]
[10 20]
[20 30 40]
[20 30]
[10 20 30 40]
slice expression,是属于左含右不含,即[)
s = s[low : high : max] 切片的三参数的意义为:
- low为截取的起始下标(含),
- high为窃取的结束下标(不含high),
- max为切片保留的原切片的最大下标(不含max);
即新切片从老切片的low下标元素开始,
len = high - low,
cap = max - low;
high 和 max一旦超出在老切片中越界,就会发生runtime err,slice out of range。
另外如果省略第三个参数的时候,那么新slice的cap,就等于cap(原slice) - low
,即cap(新) =cap(原slice) - low
package main
import "fmt"
func main() {
x := []int{10, 20, 30, 40, 50}
y := x[:2:2]
z := x[2:4:5]
fmt.Println(x)
fmt.Println(y)
fmt.Println(z)
fmt.Println("----------")
fmt.Println(cap(x))
fmt.Println(cap(y))
fmt.Println(cap(z))
}
输出
[10 20 30 40 50]
[10 20]
[30 40]
----------
5
2
3
func main() {
x := []int{10, 20, 30, 40, 50}
y := x[1:2]
z := x[2:4:5]
fmt.Println(x)
fmt.Println(y)
fmt.Println(z)
fmt.Println("----------")
fmt.Println(cap(x))
fmt.Println(cap(y))
fmt.Println(cap(z))
}
执行结果如下:
[10 20 30 40 50]
[20]
[30 40]
----------
5
4
3
Slices Share Storage & append confuse
slice include array 、length、capacity. if three aparts are all not initial,this slice is nil
func main() {
x := []int{1, 2, 3, 4}
y := x[:2]
z := x[1:]
x[1] = 20
y[0] = 10
z[1] = 30
fmt.Println("x:", x)
fmt.Println("y:", y)
fmt.Println("z:", z)
}
输出
x: [10 20 30 4]
y: [10 20]
z: [20 30 4]
对于apppend,总结一下:
append 其实是修改array的值
1.首先,修改的是哪个下标的值呢?
就是slice当前的len值,对应的下标。比如说,当前len是2,那么index=0 和 1都有值了,
那么就将append的值,放在index=2的地方
2.在append之前,需要考虑cap的问题。
如果当前要修改的index,还在array的cap(maxIndex)范围内,那么直接就,直接在原array上,进行修改
但是,如果当前要修改的index,将要超出array的cap(maxIndex)了,
那么就需要,创建一个新的array,在新array上进行修改
3.append,除了会修改array,对应的index上的值,还会增加返回的slice的len的值,让len的值加1。
注意,这里说的是,返回的slice的len值,会增加,入参的slice的len值,保持不变。这个时候,如果我们将返回体,assign给入参,那么入参的len值,也就增加了。但是,如果不将返回体,assign给入参,那么入参的len值,还是和以前一样,保持不变
package main
import "fmt"
func main() {
x := []int{10, 20, 30, 40, 50}
y := x[:2]
y = append(y,88)
fmt.Println(x)
fmt.Println(y)
fmt.Println(cap(x),cap(y))
}
输出
[10 20 88 40 50]
[10 20 88]
5 5
可以看到,parent slice 与 subslice shared memory 而且 cap是相同的
为了避免这个问题,利用full slice的第3个参数,
package main
import "fmt"
func main() {
x := []int{10, 20, 30, 40, 50}
y := x[:2:2] // 这一行,稍微变一下
y = append(y,88)
fmt.Println(x)
fmt.Println(y)
fmt.Println(cap(x),cap(y))
}
输出
[10 20 30 40 50]
[10 20 88]
5 4
Copy slice
x := [] int{ 1 , 2 , 3 , 4 }
y := make([] int , 4 )
num := copy(y , x )
fmt.Println ( y , num)
You get the output: [1 2 3 4] 4
The copy function takes two parameters. The first is the destination slice and the second is the source slice.
It copies as many values as it can from source to destination, limited by whichever slice is smaller, and returns the number of elements copied.
The capacity of x and y don’t matter, it’s the length that’s important.
Strings and Runes and Bytes
You might think that a string in Go is made out of runes, but that’s not the case. Under the covers, Go
uses a sequence of bytes to represent a string.
在Go中,默认的character encoding 是UTF-8,而UTF字符,使用1 to 4 bytes 来表示。常见的x
a
h
这些字母,都只需要1个byte。但是,一些中文,甚至emojis字符,需要多个byte来表示。比如:
func main() {
var name string = "卫鹏"
fmt.Println(len(name)) // 6
}
这个len函数,输出的结果,不是2,而是6。这就表示 卫鹏 这2个中文,每个中文字符使用3个byte来表示,2个中文字符,就需要6个byte来表示。
而string 的slice expression,他是按照byte来截取的。如果在需要多个byte来表示 一个UTF字符时,可能会出现,意想不到的结果。如下:
func main() {
var name string = "Hello卫鹏"
fmt.Println(name[:5]) // Hello
fmt.Println(name[5:6]) // �
}
第一个输出,是hello,这个没问题。但是第二个输出,是一个 �
,这个是因为卫这个中文字符,有3个byte,而我们只截取其中的第1个byte,所以这个结果肯定是invalid。
通过上面的例子,我们就明白了,针对string做slice expression时,一定要注意。只有确定这个string,都是由1个byte字符组成的,我们才能做slice expression。否则,如果存在,需要使用多个byte来表示的字符,那么就容易出现问题。
int into a string
A common bug for new Go developers is to try to make an int into a string by using a type conversion:
var x int = 65
var y = string( x )
fmt . Println ( y )
This results in y having the value “A”, not “65”.
As of Go 1.15, go vet blocks a type conversion to string from any integer type other than rune or byte.
To convert an int to string in Golang,
- strconv.Itoa(): The strconv.Itoa() function converts an integer base 10 value to an ASCII string.
- strconv.FormatInt(): The strconv.Format() function converts int64 value to string.
- fmt.Sprintf(): The fmt.Sprintf() function formats according to a format specifier and returns the resulting string.
package main
import (
"fmt"
"strconv"
)
func main() {
data := 21
s := strconv.Itoa(data)
fmt.Printf("%T, %v\n", s, s) // string, 21
}
The FormatInt() is a built-in function of the strconv package that converts an integer value to a string in Golang. The FormatInt() function returns the string representation of i in the given base for 2 <= base <= 36. The result uses the lowercase letters ‘a’ to ‘z’ for digit values >= 10.
package main
import (
"fmt"
"strconv"
)
func main() {
data := int64(-19)
str10 := strconv.FormatInt(data, 10)
fmt.Printf("%T, %v\n", str10, str10) // string, -19
str16 := strconv.FormatInt(data, 16)
fmt.Printf("%T, %v\n", str16, str16) // string, -13
}
Maps
declare maps
The map type is written as map[keyType]valueType
- create a nil map
var nilMap map[string]int
In this case nilMap is declared to be a map with string keys and int values. The zero value for a map is nil.
A nil map has a length of 0. Attempting to read a nil map always returns the zero value for the map’s value type. However, attempting to write to a nil map variable causes a panic.
- create a map using an empty map literal.
totalWins := map[string]int{}
this is not the same as a nil map. It has a length of zero, but you can read and write a map assigned an empty map literal.
- declaring a map with default values
people := map[string]int{
"zhangsan": 20,
"lisi":15,
"wangwu":30,
}
A map literal’s body is written as the key, followed by a colon(:)
, then the value. There’s a comma separating each key-value pair in the map, even on the last line.
- use make to create a map with a default size
people := make(map[string]int, 10)
Maps created with make still have a length of 0, and they can grow past the initially specified size.
Reading & Writing a map
package main
import "fmt"
func main() {
teams := map[string]int{}
teams["zhangsan"] = 5
fmt.Println(teams)
fmt.Println(teams["lisi"])
teams["lisi"]++
fmt.Println(teams)
}
输出
map[zhangsan:5]
0
map[lisi:1 zhangsan:5]
Go provides the comma ok idiom to tell the difference between a key that’s associated with a zero value and a key that’s not in the map.
If ok is true, the key is present in the map. If ok is false, the key is not present.
package main
import "fmt"
func main() {
teams := map[string]int{
"zhangsan": 5,
"lisi": 0,
}
v, ok := teams["zhangsan"]
fmt.Println(v, ok)
v, ok = teams["lisi"]
fmt.Println(v, ok)
v, ok = teams["wanger"]
fmt.Println(v, ok)
}
输出
5 true
0 true
0 false
Deleting From Maps
package main
import "fmt"
func main() {
teams := map[string]int{
"zhangsan": 5,
"lisi": 0,
}
delete(teams,"lisi")
delete(teams,"wanger")
fmt.Println(teams) // map[zhangsan:5]
}
Structs
you might be wondering about the difference between classes and structs.
The difference is simple: Go doesn’t have classes, because it doesn’t have inheritance.
declaring struct
type person struct {
name string
age int
pet string
}
A struct type is defined with the keyword type, the name of the struct type, the keyword struct
, and a pair of braces ({}).
Within the braces, you list the fields in the struct. Just like we put the variable name first and the variable type second in a var declaration, we put the struct field name first and the struct field type second.
Also note that unlike map literals, there are no commas separating the fields in a struct
declaration.
create a empty struct
var fred person
wangwu:=person{}
there is no difference between assigning an empty struct literal and not assigning a value at all.
func main() {
type person struct {
name string
age int
male bool
}
var fred person
fred.name = "zhangsan"
fmt.Println(fred) // {zhangsan 0 false}
}
create a non-empty struct using struct literal.
package main
import "fmt"
func main() {
type person struct {
name string
age int
pet string
}
lisi := person{
"lisi",
15,
"dog",
}
fmt.Println(lisi)
wanger := person{
age: 90,
name: "wanger",
}
fmt.Println(wanger)
wanger.pet = "cat"
fmt.Println(wanger)
}
Any field not specified is set to its zero value.
Anonymous Structs
什么是Anonymous Structs?
declare that a variable implements a struct type ,but not first giving the struct type a name.
示例如下:
func main() {
var zhangsan struct {
name string
age int
pet string
}
zhangsan.name = "bob"
zhangsan.age = 50
zhangsan.pet = "dog"
pet := struct {
name string
kind string
}{
name: "Fido",
kind: "dog"
}
fmt.Println(pet)
}