copy函数 #
Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。
//1.不同类型的切片无法复制
//2.如果s1的长度大于s2的长度,将s2中对应位置上的值替换s1中对应位置的值
//3.如果s1的长度小于s2的长度,多余的将不做替换
func main() {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s3 := []int{6, 7, 8, 9}
copy(s1, s2)
fmt.Println(s1) //[4 5 3]
copy(s2, s3)
fmt.Println(s2) //[6 7]
}
l:=make([]string,len(s))
copy(h,s)
var, :=, new() , make()的区别 #
说明 #
go语言中,提供了多种变量声明和初始化的方法。这里着重一一说明。并提供一个简单的指南。
指南 #
- 使用
make()
,来初始化slice
,map
和channel
。 - 大多数场合,类型明确的场合下,使用短变量声明方式
:=
。 - 当使用文字方式初始化一个变量,并且需要指明类型时,使用
var
变量声明方式。 - 避免使用
new()
,除非你需要一个指针变量。
变量声明方式 #
go语言可以使用 var
来声明一个变量,并指明变量的数据类型。
// 初始化整数变量,值为10。
var v int = 10
fmt.Println(v)
// 输出: 10
// 变量声明: 一个slice变量
var vSlice []int = []int{1, 2, 3, 4}
fmt.Println(vSlice, "type: ", reflect.TypeOf(vSlice).Kind())
// 输出: [1 2 3 4] type: slice
// 短变量声明: 一个map变量,指向的值为[]
var vMap map[string]int = map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(vMap)
// 输出: map[a:1 b:2]
短变量声明方式 #
short variable declarations 符号: :=
。
短变量声明时,变量的默认类型是: bool
, rune
, int
, float64
, complex128
or string
// 短变量声明: 一个整数变量。
sdvInt := 10
fmt.Println(sdvInt, "type: ", reflect.TypeOf(sdvInt).Kind())
// 输出: 10 type: int
// 短变量声明: 一个slice变量
sdvSlice := []int{1, 2, 3, 4}
fmt.Println(sdvSlice, "type: ", reflect.TypeOf(sdvSlice).Kind())
// 输出: [1 2 3 4] type: slice
// 短变量声明: 一个map变量,指向的值为[]
sdvMap := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(sdvMap)
// 输出: map[a:1 b:2]
new(T) #
new(T)
的特点:
- 根据类型T分配内存
- 设置内存为0
- 返回内存的指针
// 初始化一个整数指针变量,指向的值为0
var i3 *int = new(int)
fmt.Println(*i3)
// 初始化一个slice指针变量
var i4 = new([10]int)[0:5]
fmt.Println(i4, "type: ", reflect.TypeOf(i4).Kind())
// 输出: [0 0 0 0 0] type: slice
// 初始化一个map指针变量,指向的值为[]
var i5 *map[string]int = new(map[string]int)
fmt.Println(*i5)
// 输出: map[]
// 初始化一个chan指针变量,指向的值为nil
var i6 *chan int = new(chan int)
fmt.Println(*i6)
// 输出: nil
make() #
make只用于初始化 slice
,map
和 channel
。
// make只能用于创建slice, map, channel
// 切片类型(slice)
makeSlice := make([]int, 5, 10)
fmt.Println(makeSlice)
// 输出: [0 0 0 0 0]
// Map 类型
var makeMap map[string]int = make(map[string]int)
fmt.Println(makeMap)
// 输出: map[]
// Channel 类型
var makeChan chan int32 = make(chan int32, 100)
fmt.Println(makeChan)
// 输出: 0xc000112000
完整源码 #
package main
import (
"fmt"
"reflect"
)
func main() {
// 初始化整数变量,值为10。
var v int = 10
fmt.Println(v)
// 输出: 10
// 变量声明: 一个slice变量
var vSlice []int = []int{1, 2, 3, 4}
fmt.Println(vSlice, "type: ", reflect.TypeOf(vSlice).Kind())
// 输出: [1 2 3 4] type: slice
// 短变量声明: 一个map变量,指向的值为[]
var vMap map[string]int = map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(vMap)
// 输出: map[a:1 b:2]
// 短变量声明: 一个整数变量。
sdvInt := 10
fmt.Println(sdvInt, "type: ", reflect.TypeOf(sdvInt).Kind())
// 输出: 10 type: int
// 短变量声明: 一个slice变量
sdvSlice := []int{1, 2, 3, 4}
fmt.Println(sdvSlice, "type: ", reflect.TypeOf(sdvSlice).Kind())
// 输出: [1 2 3 4] type: slice
// 短变量声明: 一个map变量,指向的值为[]
sdvMap := map[string]int{
"a": 1,
"b": 2,
}
fmt.Println(sdvMap)
// 输出: map[a:1 b:2]
// 初始化一个整数指针变量,指向的值为0
var newInt *int = new(int)
fmt.Println(*newInt)
// 初始化一个slice指针变量
var newSlice = new([10]int)[0:5]
fmt.Println(newSlice, "type: ", reflect.TypeOf(newSlice).Kind())
// 输出: [0 0 0 0 0] type: slice
// 初始化一个map指针变量,指向的值为[]
var newMap *map[string]int = new(map[string]int)
fmt.Println(*newMap)
// 输出: map[]
// 初始化一个chan指针变量,指向的值为nil
var newChan *chan int = new(chan int)
fmt.Println(*newChan)
// 输出: nil
// make只能用于创建slice, map, channel
// 切片类型(slice)
makeSlice := make([]int, 5, 10)
fmt.Println(makeSlice)
// 输出: [0 0 0 0 0]
// Map 类型
var makeMap map[string]int = make(map[string]int)
fmt.Println(makeMap)
// 输出: map[]
// Channel 类型
var makeChan chan int32 = make(chan int32, 100)
fmt.Println(makeChan)
// 输出: 0xc000112000
}
print、println、printf的区别 #
Print 和 Println 这两个打印方式类似,只在格式上有区别
- Println 打印的每一项之间都会有空行,Print 没有,例如:
fmt.Println("go","python","php","javascript") // go python php javascript
fmt.Print("go","python","php","javascript") // gopythonphpjavascript
- Println 会自动换行,Print 不会,例如:
fmt.Println("hello")
fmt.Println("world")
// hello
// world
fmt.Print("hello")
fmt.Print("world")
// helloworld
Println 和 Printf
func main() {
a:=10
b:=20
c:=30
fmt.Println("a=", a , ",b=" , b , ",c=" , c)
fmt.Printf("a=%d,b=%d,c=%d" , a , b , c)
}
二维数组去重 #
import (
"fmt"
"reflect"
)
func killRepetion(nums [][]int) [][]int {
newRes := make([][]int, 0)
for i := 0; i < len(nums); i++ {
flag := false
for j := i + 1; j < len(nums); j++ {
if reflect.DeepEqual(nums[i], nums[j]){
flag = true
break
}
}
if !flag {
newRes = append(newRes, nums[i])
}
}
return newRes
}
func main() {
result := [][]int{{1,2,3},{1,2,3},{1,2,3},{1,2,2}}
killDoble := killRepetion(result)
fmt.Println(killDoble)
}
其中reflect函数中的 reflect.DeepEqual(a[], b[])可以比较a数组和b数组是否相同
序列化和反序列化 #
json.Unmarshall解析json字符串 #
var mat MaterialInfo
err := json.Unmarshal([]byte(args[0]), &mat)
if err != nil {
return shim.Error("反序列化信息时发生错误")
}
[]byte(args[0]) 字符串切片,将arg[0]中的字符串存储在切片中
json.Unmarshall 解析json字符串
marshal与unmarshal序列化与反序列化 #
type Stu struct {
Name string `json:"name"`
Age int
HIgh bool
sex string
Class *Class `json:"class"`
}
type Class struct {
Name string
Grade int
}
func main() {
//实例化一个数据结构,用于生成json字符串
stu := Stu{
Name: "张三",
Age: 18,
HIgh: true,
sex: "男",
}
//指针变量
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu.Class=cla
//Marshal失败时err!=nil
jsonStu, errs := json.Marshal(stu)
if errs != nil {
fmt.Println("生成json字符串错误")
}
//jsonStu是[]byte类型,转化成string类型便于查看
fmt.Println(string(jsonStu))
data:="{\"name\":\"张三\",\"Age\":18,\"high\":true,\"sex\":\"男\",\"CLASS\":{\"naME\":\"1班\",\"GradE\":3}}"
str:=[]byte(data)
//1.Unmarshal的第一个参数是json字符串,第二个参数是接受json解析的数据结构。
//第二个参数必须是指针,否则无法接收解析的数据,如stu仍为空对象StuRead{}
//2.可以直接stu:=new(StuRead),此时的stu自身就是指针
stus:=Stu{}
err:= json.Unmarshal(str, &stus)
if err!=nil{
fmt.Println(err)
}
fmt.Println(stu)
fmt.Println(stu.Age)
fmt.Println(stu.Class)
}
{"name":"张三","Age":18,"HIgh":true,"class":{"Name":"1班","Grade":3}}
{张三 18 true 男 0xc0000a6018}
18
&{1班 3}
type StuRead struct {
Name interface{} `json:"name"`
Age interface{}
HIgh interface{}
sex interface{}
Class interface{} `json:"class"` //interface{} 类型,空接口
}
func main() {
data:="{\"name\":\"张三\",\"Age\":18,\"HIgh\":true,\"sex\":\"男\",\"class\":{\"Name\":\"1班\",\"Grade\":3}}"
str:=[]byte(data)
stu:=StuRead{}
err:=json.Unmarshal(str,&stu)
if err!=nil{
fmt.Println(err)
}
fmt.Println(stu)
fmt.Println(stu.Age)
}
{张三 18 true <nil> map[Grade:3 Name:1班]}
18
Go的次方实现 #
以2的3次方为例:
a := math.Pow(2, 3)
特殊情况 #
当遇到要求 2 的 n 次方的时候,我们可以运用 Go 语言的左移运算符 « ,实现左移运算。
左移的运算规则是左移 N 位,就是乘以 2 的 N 次方。例子如下:
左移 « #
a := 1 << 3 // 2的3次方*1
b := 1 << 6 // 2的6次方*1 64
c := 4 << 2 // 2的2次方*4 16
d := 4 << 3 // 2的3次方*4 32
右移 » #
右移的运算规则是右移 N 位,就是除以 2 的 N 次方。
a := 16 >> 3 // 16除以2的3次方
1
list(列表) #
列表是一种非连续存储的容器,又多个节点组成,节点通过一些变量将彼此串联起来。列表底层常见的数据结构有:单链表、双链表等;go语言中,列表的实现都在 container/list
包中,内部实现原理是双链表。
初始化 #
变量名 := list.New()
var 变量名 = list.List
PS: 列表和 map (字典) 有什么区别?
相比较 map (字典),列表没有具体元素类型的限制,也就是说,你可以添加任意类型到 list 容器中,如字符串、整型等。这带来了一些便利,但是也存在一些问题:给一个列表添加了非期望类型的值后,在取值时,将 interface{} 转换为期望类型时会发生宕机。
向列表中添加元素 #
双链表支持往队列前面或后面添加元素,对应的方法分别是:
PushFront
PushBack
示例代码如下:
l := list.New()
l.PushFront("cc") //队列前面添加元素
l.PushBack("dd") //队列后面添加元素
关于 list (列表) 插入元素的方法,如下表所示:
方法 | 功能 |
---|---|
InsertAfter(v interface{}, mark *Element) *Element |
在 mark 点后面插入元素 |
InsertBefore(v interface{}, mark *Element) *Element |
在 mark 点前面插入元素 |
PushFrontList(other *List) |
添加 other 列表中的元素到头部 |
PushBackList(other *List) |
添加 other 列表中的元素到尾部 |
从 list (列表) 中删除元素 #
list (列表) 的插入函数的返回值是一个 *list.Element
结构,通过它来完成对列表元素的删除:
package main
import (
"container/list"
)
func main() {
l := list.New()
// 头部添加字符串
l.PushFront("cc")
// 尾部添加字符串
l.PushBack("dd")
// 尾部添加一个整型,并保持元素句柄
element := l.PushBack(1)
// 在 1 之后添加字符串 2
l.InsertAfter("2", element)
// 在 1 之前添加字符串 0
l.InsertBefore("0", element)
// 删除 element 对应的元素
l.Remove(element)
}
最终队列中保存的元素有:
cc dd 0 2
遍历 list (列表) #
遍历 list (列表) 需要搭配 Front()
函数获取头元素,遍历过程中,只要元素不为空则可继续调用 Next
函数往下遍历:
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
// 头部添加字符串
l.PushFront("cc")
// 尾部添加字符串
l.PushBack("dd")
// 遍历
for i := l.Front(); i != nil; i = i.Next() {
fmt.Println(i.Value)
}
}
注意,在 for 语句遍历中:
- 其中
i := l.Front()
表示初始赋值,用来获取列表的头部下标; - 然后每次会循环会判断
i != nil
,若等于空,则会退出循环,否则执行i.Next()
继续循环下一个元素;
代码输出如下:
cc
dd
键盘获取 #
func main() {
var name string
var age int
_, _ = fmt.Scanln(&name, &age)
fmt.Printf("我叫 %s, 今年 %d 岁!", name, age)
}
var (
a string
b int
c bool
)
func test_fmt() {
n, err := fmt.Scanf("%s %d %t", &a, &b, &c) // hello 10 true
// fmt.Scanf会将你通过空格分隔的字符串填充到对应的为, 返回n表示正确填充数, err表示是否出错,遇到换行结束
fmt.Println(n, err)
fmt.Println(a, b, c)
}
传入一维数组 #
func main() {
var n, tmp int
fmt.Scanln(&n)
marry := make([]int, 0, n)
for i := 0; i < n; i++ {
fmt.Scanln(&tmp)
marry = append(marry, tmp)
}
fmt.Println(marry)
}
传入二维数组 #
func main() {
var n, m, tmp int
fmt.Scanf("%v %v", &n, &m)
way := make([][]int, 0, n)
for i := 0; i < n; i++ {
args := make([]int, 0, m)
for j := 0; j < m; j++ {
fmt.Scanf("%v", &tmp)
args = append(args, tmp)
}
way = append(way, args)
}
fmt.Println("way", way)
}
实现error接口 #
在errors包下,有封装好了的方法直接在里面写上错误信息就可以
err=errors.New("我写错了")
//包内部 就是实现了error方法
package errors
func New(text string)error{
return &errorString{text}
}
type errorString struct{
s string
}
func (e *errorString)Error()string{
return e.s
}