Administrator
发布于 2023-03-06 / 24 阅读
0
0

Golang---Composite Types

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]inta 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,

  1. strconv.Itoa(): The strconv.Itoa() function converts an integer base 10 value to an ASCII string.
  2. strconv.FormatInt(): The strconv.Format() function converts int64 value to string.
  3. 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)
}

评论