使用Go编写第一行代码
新建golangFile文件夹,在文件夹中新建main.go,在里面写入如下代码
package main
import "fmt"
func main() {
fmt.Println("hellow world")
}
编译
go build main.go
或者直接编译当前包含go代码的文件夹
go build
会看到编译后文件夹内多出
main.exe或者golangFile.exe
直接在终端执行就会看到“hello world”了
.\main.exe
如果想查看编译过程
go build -x main.go
如果想指定一个编译后的二进制文件名称
go build -x -o hello.exe main.go
如果省去手动编译过程,一步编译+执行
go run main.go
这样直接在一个其它临时的目录生成.exe并执行出结果
定义变量
*匿名变量 _ :是一个go已定义的变量,本身这种变量不会进行空间分配,也不会占用一个变量的名字。
相当于一个黑洞,对它的任何赋值都会被吃掉不保存,通常来用作占位符
package main
import "fmt"
//定义包级别的变量
var (
name string = "yxp"
age int = 29
)
//定义变量可以省略类型,编译器根据字面量去猜这个变量的类型
var (
name2 = "zxm"
age2 = 28
)
//不推荐这种在一行里定义多个变量
var name3, age3 = "hhh", 29
func main() {
//定义函数内部变量
var test string
//变量的初始化
test = "asd"
fmt.Println(test)
fmt.Println(name)
//这是一个简短声明,简短声明只能在函数内部
yyds := true
//简短声明多个变量,其中的变量至少有一个是未定义的
_, num2 := 1, 2
fmt.Println(yyds)
//赋值
test, name = "this is test", "this is name"
fmt.Println(test, name)
//小tips,交换test和name的值
test, name = name, test
fmt.Println(test, name)
}
常量
package main
import "fmt"
func main() {
const NAME string = "yxp"
//可以省略类型
const AGE = 18
//定义多个常量
const (
AGE1 int = 19
NAME1 string = "yyy"
)
//可以省略类型
const (
AGE2 = "yyds"
NAME2 = 20
)
//也可以这样
const AGE3, NAME3 = 21, "jkl"
//同类型的常量第一个赋值之后,后面的常量值和第一个值相等
const (
AGE4 int = 1
AGE5
AGE6
)
fmt.Println(AGE4, AGE5, AGE6)
//也可以这样写
const (
A1 int = 1
A2
A3
A4 float64 = 0.1
A5
A6
)
fmt.Println(A1, A2, A3, A4, A5, A6)
//枚举,const_iota
const (
B1 int = iota //0
B2 //1
B3 //2
)
fmt.Println(B1, B2, B3)
const (
C1 int = (iota + 1) * 100 //100
C2 //200
C3 //300
)
fmt.Println(C1, C2, C3)
}
作用域、块
package main
import "fmt"
func main() {
//作用域、块。由{}来划分
outer := "outer"
outDeclare := "outDeclare"
{
inner := "inner"
//里面可以访问外面的
fmt.Println(outer)
fmt.Println(inner)
//在里面改变外面的
outer = "ch_outer"
fmt.Println(outer)
//块内重新定义块外的变量
outDeclare := "bloack_outDeclare"
fmt.Println(outDeclare)
}
fmt.Println(outer)
fmt.Println(outDeclare)
//外面不能访问里面的
//fmt.Println(inner)
}
布尔类型变量
package main
import "fmt"
func main() {
//不初始化默认是false
var zero bool
//布尔类型只有两个值true、false。不会像别的语言可以是0和1
//布尔类型占用空间1字节
isBoy := true
isGirl := false
fmt.Println(zero, isBoy, isGirl)
}
int类型变量
package main
import "fmt"
func main() {
var age int = 31
fmt.Printf("%T %d\n", age, age)
//打印查看八进制和十六进制
fmt.Println(0777, 0x10)
//位运算
//十进制转换二进制 辗转相除法(请百度)
//十进制7转换成二进制
//7/2=3...1, 3/2=1...1, 1/2=0...1
//0111
//十进制2转二进制
//2/2=1...0
//10
//7 & 2 => 0111 & 0010 => 0010 就是十进制的2
//7 | 2 => 0111 | 0010 => 0111 就是十进制的7
//7 ^ 2 => 0111 ^ 0010 => 0101 就是十进制的5
//2 << 1 => 0010 << 1 => 0100 就是十进制的4
//2 >> 1 => 0010 >> 1 => 0001 就是十进制的1
//7 &^ 2 => 0111 &^ 0010 => 0101就是十进制的5;
//&^ 按位清空,就是两个数字按位一一对应,第二个数字为1的位置,第一个数字就要为0,结果取第一个数字改变后的样子
fmt.Println(7 & 2)
fmt.Println(7 | 2)
fmt.Println(7 ^ 2)
fmt.Println(2 << 1)
fmt.Println(2 >> 1)
fmt.Println(7 &^ 2)
//int/uint/byte/rune/int*
var intA int = 10
var uintB uint = 3
fmt.Println(intA + int(uintB)) //两种类型的数需要强制转换成一种才能计算
fmt.Println(uint(intA) + uintB)
//强制类型转换要注意会溢出丢失,从大往小转
var intC int = 0xfffff
fmt.Println(intC, uint8(intC))
//byte,rune。实际上也是int(百度搜索这两种类型,理解的不太清楚)
var a byte = 'A'
var w rune = '中'
fmt.Println(a, w)
fmt.Printf("%T %d %b %o %x\n", age, age, age, age, age)
fmt.Printf("%T %c\n", a, a)
fmt.Printf("%T %q %U %c\n", w, w, w, w)
age = 1234
fmt.Printf("%5d\n", age) //一共占5位,不够在前面补空格,超出不处理
fmt.Printf("%05d\n", age) //一共占5位,不够在前面补0
fmt.Printf("%-5d\n", age) //左对齐,在右边补位。默认是右对齐,在左边补位
}
byte和rune详解
他们都属于别名类型,byte是uint8(无符号8位整数)的别名类型,rune是int32(有符号32位整数)的别名类型
package main
import (
"fmt"
)
func main() {
//rune类型可以表示一个Unicode字符,rune类型的值需要用''包裹
//直接使用Unicode支持的字符赋值,这种表示方法最通用,其他的的了解就好
var char rune = '赞'
//一个Unicode代码点通常由“U+”和一个以十六进制表示法表示的整数表示。例如,英文字母“A”的Unicode代码点为“U+0041”。
fmt.Printf("字符 '%c' 的Unicode代码点是 %s。\n", char1, ("U+8D5E"))
var ch1 rune = '\101'//表示字母A,"\"加三位八进制数,只能表示值在[1,255]内的字符
var ch2 rune = '\x41'//表示字母A,"\x"加两个十六进制数,只能表示ASCLL支持的字符
var ch3 rune = '\u90DD'//表示“郝”,'\u'加四位十六进制数,只能表示编码值在[0,65535]内的字符
var ch4 rune = '\U000090DD'//表示“郝”,'\U'加四位十六进制数,可表示任何Unicode字符
fmt.Println(char,ch1,ch2,ch3,ch4)
fmt.Printf("char:%T %c\n", char, char);
fmt.Printf("ch1:%T %c\n", ch1, ch1);
fmt.Printf("ch2:%T %c\n", ch2, ch2);
fmt.Printf("ch3:%T %c\n", ch3, ch3);
fmt.Printf("ch4:%T %c\n", ch4, ch4);
//rune类型值的表示中支持转义字符
var ch5 rune = '\''//单引号,Unicode代码点U+0027
var ch6 rune = '"'//双引号,Unicode代码点U+0022
var ch7 rune = '\\'//反斜杠,Unicode代码点U+005c
var ch8 rune = '\r'//回车,Unicode代码点U+000D
var ch9 rune = '\t'//水平制表符,Unicode代码点U+0009
var ch10 rune = '\n'//换行符,Unicode代码点U+000A
var ch11 rune = '\b'//退格符,Unicode代码点U+0008
var ch12 rune = '\a'//告警铃声或蜂鸣声,Unicode代码点U+0007
fmt.Printf("ch5:%T %c\n", ch5, ch5);
fmt.Printf("ch6:%T %c\n", ch6, ch6);
fmt.Printf("ch7:%T %c\n", ch7, ch7);
fmt.Printf("ch8:%T %c\n", ch8, ch8);
fmt.Printf("ch9:%T %c\n", ch9, ch9);
fmt.Printf("ch10:%T %c\n", ch10, ch10);
fmt.Printf("ch11:%T %c\n", ch11, ch11);
fmt.Printf("ch12:%T %c\n", ch12, ch12);
}
浮点类型变量
package main
import "fmt"
func main() {
//float32,float64只有这两种类型
var high float64
fmt.Printf("%T %f\n", high, high)
fmt.Println(1.1 + 1.2)
fmt.Println(1.1 - 1.2)
fmt.Println(1.1 * 1.2)
fmt.Println(1.1 / 1.2) //0.9166666666666666
high = 1.64
high++
//浮点数计算是有精度损耗的,不准确。结果是2.6399999999999997
fmt.Println(high)
fmt.Printf("%T %5.2f\n", high, high) //保留5位2个小数点
//关系运算
//浮点数一般不计算== !=,因为精度损耗结果可能不准确
//一般计算 > >= < <=
//如果要计算== != 请百度搜搜
}
字符串类型变量(含切片和索引)
package main
import "fmt"
func main() {
//特殊字符 \n \f \t \r...在""里可以被转义,在``里不会被转义
var name string = "as\tdasa" //可解释字符串
var desc string = `aaa\taa` //原生字符串
fmt.Println(name, desc)
//字符串链接
fmt.Println("我叫" + "hhh")
//fmt.Println("我叫" + 'Q')//报错 字符串和字符不能连接
//字符串之间的比较
fmt.Println("a" == "b")
fmt.Println("a" > "b")
fmt.Println("a" >= "b")
fmt.Println("a" < "b")
fmt.Println("a" <= "b")
fmt.Println("a" != "b")
//使用索引或者切片操作,字符串定义的内容只能为ASCII
desc = "abcdefg"
//索引为从0~(n-1)
fmt.Printf("%T %c\n", desc[0], desc[0]) //a
//切片[strat:end],取出从[star ~ (end-1)]之内的字符
fmt.Printf("%T %s\n", desc[0:3], desc[0:3]) //abc
//如果不是ASCII
desc = "哈哈哈"
fmt.Printf("%T %c\n", desc[0], desc[0]) //乱码
fmt.Printf("%T %s\n", desc[0:2], desc[0:2]) //乱码
//得到字符串长度,也要是ASCII字符串,实际上len()得到的是字符串所占字节
desc = "abc"
fmt.Println(len(desc)) //3
desc = "几十块"
fmt.Println(len(desc)) //9
}
指针类型变量
package main
import "fmt"
func main() {
a := 2
var b int
fmt.Println(a, b) //2 0
b = 3
fmt.Println(a, b) //2 3
//指针
var c *int
c = &a
//c := &a
fmt.Printf("%T %d\n", c, c)
fmt.Printf("%T %d\n", *c, *c)
//通过指针修改变量a的值
*c = 4
fmt.Println(a, *c)
}
scan方法,用户输入方法
package main
import "fmt"
//用户输入
func main() {
var name string
fmt.Println("请输入名字")
fmt.Scan(&name)
fmt.Println("您输入的内容是:" + name)
//输入内容要保证是int,不是int会输出0
var age int
fmt.Println("请输入年龄")
fmt.Scan(&age)
fmt.Println("年龄是:", age)
//如果上一个输入的类型错误,那么这里也会使用上一个错的数据类型自动输入
var height string
fmt.Println("请输入身高")
fmt.Scan(&height)
fmt.Println("身高是:", height)
}
if 语句
语法
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
func main() {
x := 0
if n := "abc"; x > 0 {//定义了局部变量n,只能在块内访问
fmt.Printf("%T : %c", n[2], n[2])
} else if x < 0 { // 注意 else if 和 else 左大括号位置。
fmt.Printf("%T : %c", n[1], n[1])
} else {
fmt.Printf("%T : %c", n[0], n[0])
}
}
不支持三元操作符(三目运算符) "a > b ? a : b"。
switch语句
Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。
switch var1 {
case val1:
...
case val2:
...
default:
...
}
变量 var1 可以是任何类型,而 val1 和 val2 则可以是<u>同类型的</u>任意值。类型不被局限于常量或整数,但<u>必须是相同的类型;或者最终结果为相同类型的表达式</u>。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。
var grade string = "B"
var marks int = 90
switch marks {
case 90:
grade = "A"
case 80:
grade = "B"
case 50, 60, 70:
grade = "C"
default:
grade = "D"
}
//省略条件表达式,可当 if...else if...else
switch {
case grade >= "A":
fmt.Printf("优秀\n")
case grade >= "B":
fmt.Printf("良好\n")
case grade >= "D":
fmt.Printf("及格\n")
case grade <= "D":
fmt.Printf("不及格\n")
}
//fallthrough
var k = 0
switch k {
case 0:
println("fallthrough")
fallthrough
/*
Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
而如果switch没有表达式,它会匹配true。
Go里面switch默认相当于每个case最后带有break,
匹配成功后不会自动向下执行其他case,而是跳出整个switch,
但是可以使用fallthrough强制执行后面的case代码。
*/
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("def")
}
Type Switch
switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
格式:
switch x.(type){
case type:
statement(s)
case type:
statement(s)
/* 你可以定义任意个数的case */
default: /* 可选 */
statement(s)
}
实例:
var x interface{}
switch i := x.(type) { // 带初始化语句
case nil:
fmt.Printf(" x 的类型 :%T\r\n", i)
case int:
fmt.Printf("x 是 int 型")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型")
default:
fmt.Printf("未知型")
}
for循环结构
# for循环结构
```go
package main
func main() {
s := "abc"
// 常见的 for 循环,支持初始化语句。
for i, n := 0, len(s); i < n; i++ {
println(s[i])
}
// 替代 while (n > 0) {}
n := len(s)
for n > 0 {
println(s[n-1]) // 替代 for (; n > 0;) {}
n--
}
// 替代 while (true) {}
// 替代 for (;;) {}
for {
println(1)
}
}
for range
s := "abc"
//忽略 value,支持 string/array/slice/map。
for i := range s {
println(s[i])
}
//忽略 key。
for _, c := range s {
println(c)
}
// 忽略全部返回值,仅迭代。
for range s {
}
m := map[string]int{"a": 1, "b": 2}
// 返回 (key, value)。
for k, v := range m {
println(k, v)
}
9*9乘法表
package main
import "fmt"
func main() {
var a int = 0
var b int = 0
for a = 1; a < 10; a++ {
for b = 1; b < 10; b++ {
var res int
if b <= a {
res = a * b
fmt.Printf("%d*%d=%d ", b, a, res)
}
}
fmt.Printf("\n")
}
}
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
数组
package main
import "fmt"
func main() {
//定义数组
var num [10]int
var boolType [10]bool
var strType [10]string
//查看类型和每个数组单元默认值是什么
fmt.Printf("%T %d\n", num, num)
fmt.Printf("%T %t\n", boolType, boolType)
fmt.Printf("%T %s\n", strType, strType)
/*数组字面量的三种表示方法*/
//只定义前几个数组元素
num = [10]int{1, 2, 3}
fmt.Println(num)
//指定数组某个单元的值
num = [10]int{1: 10, 8: 80, 9: 90}
fmt.Println(num)
//不声明数组的长度,数组字面量的元素个数要和数组变量num能容纳的元素个数一致
num = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(num)
/*数组的计算*/
arr1 := [...]int{1, 2, 3}
arr2 := [...]int{1, 2, 2}
fmt.Println(arr1 == arr2)
//len()获得数组的长度
arr3 := [...]string{"asd", "aaa"}
fmt.Println(len(arr3))
//索引
arr3[1] = "hhahah"
fmt.Println(arr3[0])
//字符串切片
var s string = "qwertyuiop"
fmt.Printf("%T %v\n", s[0:4], s[0:4])
//数组切片
var arr4 [10]int
arr4 = [10]int{1, 2, 3, 4}
fmt.Printf("%T %v\n", arr4[0:4], arr4[0:4])
//多维数组
var arr5 [3][2]int
fmt.Printf("%T %v\n", arr5, arr5)
arr5 = [3][2]int{{1}, {2, 2}}
fmt.Printf("%T %v\n", arr5, arr5)
}
切片slice
需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。
------------有时间详细了解切片到底是不是指针
nil 简单来说:
nil是一个预先声明的标识符,表示指针、通道、函数、接口、映射或切片类型。
nil可以代表很多类型的零值
------------有时间详细总结下nil
//定义一个切片
var num []int
fmt.Printf("%T len:%d cap:%d\n", num, len(num), cap(num))
if num == nil {
fmt.Println("未初始化的切片num 等于 nul,其实切片底层就是一个指针,指针的默认值是空,所以这里切片默认值就是空")
}
//切片字面量,和初始化
num = []int{1, 2, 3}
fmt.Printf("初始化切片:%#v len:%d cap:%d\n", num, len(num), cap(num))
//切片可长度可变
num = []int{1, 2, 3, 4}
fmt.Printf("改变了切片的长度:%#v len:%d cap:%d\n", num, len(num), cap(num))
//数组长度不可变,以下代码会报错
//var arr = [3]int{1, 2, 3}
//arr = [2]int{1, 2}
//数组切片赋值给切片
var arr1 [10]int = [10]int{1, 2, 3, 4, 5}
num = arr1[0:8]
fmt.Printf("通过数组得到的切片:%#v len:%d cap:%d\n", num, len(num), cap(num))
//make函数
num = make([]int, 3)
fmt.Printf("通过make函数只声明len的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
num = make([]int, 3, 5)
fmt.Printf("通过make函数声明了len和cap的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
/*
切片为什么要有容量的概念?为什么不直接使用长度len
因为切片是可变长的,每次变长都会重新生在内存开辟一片更大的空间,然后把这个空间的地址重新赋值给变量
那么如果每增加一个元素就新开辟空间再赋值有点浪费计算机资源
所以可以生成冗余的空间,我们当前用到几个单元就是len,如果需要增加元素就从cap中再给一个len就ok
*/
/*切片的操作,增、删、改、查*/
//查
fmt.Printf("根据索引查看切片元素:%d\n", num[0])
//fmt.Println(num[4]) //无法查到4,index out of range [4] with length 3
//改
num[0] = 10
fmt.Printf("查看通过索引改变了的切片元素:%d\n", num[0])
//增
num = append(num, 333)
fmt.Printf("查看append之后的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
//超出切片容量之后,会自动扩展容量。底层其实是重新申请一个更大的内存空间,并把空间的地址赋值给切片变量
num = append(num, 444, 555)
fmt.Printf("查看append了超出原有容量的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
//切片的切片
newNum := num[1:3]
//new_cap = 10-1
fmt.Printf("查看切片的切片:%#v new_len:%d new_cap:%d \n", newNum, len(newNum), cap(newNum))
newNum = num[1:3:6]
//new_cap = 6-1
fmt.Printf("查看切片的切片:%#v new_len:%d new_cap:%d \n", newNum, len(newNum), cap(newNum))
//切片的切片容量不能设置的比原来的大
//n := num[1:3:100]//这是会报错的
//遍历切片
for k, v := range num {
fmt.Println(k, v)
}
/*
切片的副作用
切片其实是使用源的地址。而不是把源的值复制一份使用
*/
//切片的切片被改
num = make([]int, 3, 5)
num01 := num[1:3]
fmt.Println(num, num01)
num01[0] = 1
fmt.Println(num, num01)
//增加num01的元素
num01 = append(num01, 111)
fmt.Println(num, num01)
//增加num的元素
num = append(num, 222)
fmt.Println(num, num01)
//直到num或num1之中的哪一个先append超出了cap容量,那么就会重新分配空间,两者才会分开
num01 = append(num01, 333, 444)
fmt.Println(num, num01)
//数组的切片被改
var arr2 = [5]int{0, 1, 2, 3, 4}
var num02 = arr2[:] //只写一个[:]代表生成一个和原数组一样的切片
fmt.Println(arr2, num02)
num02[0] = 111
fmt.Println(arr2, num02)
//删-copy。在go中常用copy来实现删除切片元素
sArr := []int{1, 2, 3}
sArr1 := []int{11, 22, 33, 44}
copy(sArr, sArr1) //把sArr1 copy到sArr上
fmt.Printf("使用copy的效果:%v\n", sArr)
//删除切片第一个元素和最后一个元素
sArr2 := []int{1, 2, 3, 4, 5}
fmt.Printf("删除切片第一个元素:%v\n", sArr2[1:])
fmt.Printf("删除切片最后一个元素:%v\n", sArr2[0:len(sArr2)-1])
//删除切片的中间元素
copy(sArr2[2:], sArr2[3:])
sArr2 = sArr2[0 : len(sArr2)-1]
fmt.Printf("删除sArr2[2]元素:%v\n", sArr2)
/*利用append和删除首尾元素的方法实现,堆栈和队列*/
//队列-先进先出
queue := []int{}
fmt.Println(queue)
queue = append(queue, 1)
fmt.Println(queue)
queue = append(queue, 2)
fmt.Println(queue)
queue = append(queue, 3)
fmt.Println(queue)
queue = queue[1:]
fmt.Println(queue)
queue = queue[1:]
fmt.Println(queue)
queue = queue[1:]
fmt.Println(queue)
//堆栈-先进后出
stack := []int{}
fmt.Println(stack)
stack = append(stack, 1)
fmt.Println(stack)
stack = append(stack, 2)
fmt.Println(stack)
stack = append(stack, 3)
fmt.Println(stack)
stack = stack[0 : len(stack)-1]
fmt.Println(stack)
stack = stack[0 : len(stack)-1]
fmt.Println(stack)
stack = stack[0 : len(stack)-1]
fmt.Println(stack)
总结:
1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
2. 切片的长度可以改变,因此,切片是一个可变的数组。
3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
6. 如果 slice == nil,那么 len、cap 结果都等于 0。
多维切片
//第一种声明方式
point := [][]int{}
fmt.Printf("%T\n", point)
point = [][]int{{1, 2}, {2, 23}, {1, 2, 3, 4, 5}}
fmt.Println(point)
point[1][1] = 222
fmt.Println(point)
//第二种声明方式
point2 := make([][]int, 2, 4)
fmt.Printf("%T %d %d\n", point2, len(point2), cap(point2))
point2 = append(point2, []int{1, 1, 1, 1, 1})
fmt.Println(point2)
[][]int
[[1 2] [2 23] [1 2 3 4 5]]
[[1 2] [2 222] [1 2 3 4 5]]
[][]int 2 4
[[] [] [1 1 1 1 1]]
切片和数组的传值类型对比
//改变slice02会影响到slice01,因为slice01赋值给slice02其实是把地址给了slice02,他俩指向同一片内存空间
var slice01 []int = []int{1, 2, 3, 4}
var slice02 []int = slice01
fmt.Println(slice01, slice02)
slice02[0] = 10000
fmt.Println(slice01, slice02)
slice02 = []int{1, 1, 1} //这里改变slice02,相当于把slice02重新指向了一个新地址
fmt.Println(slice01, slice02) //所以slice01不会跟着变了
//改变arr02就不会影响到arr01,arr01和arr02指向不同的内存空间
var arr01 [5]int = [5]int{1, 1, 1, 1, 1}
var arr02 = arr01
arr02[0] = 222
fmt.Println(arr01, arr02)
[1 2 3 4] [1 2 3 4]
[10000 2 3 4] [10000 2 3 4]
[10000 2 3 4] [1 1 1]
[1 1 1 1 1] [222 1 1 1 1]
sort模块的排序函数
sort模块可以对切片进行排序,请查官网手册的sort模块
num := []int{2, 4, 3, 6, 678, 31}
sort.Ints(num)
fmt.Println(num)
str := []string{"a1a", "asd", "34", "hhh"}
sort.Strings(str)
fmt.Println(str)
fnum := []float64{-1, 2.2, 391.01, 0.0, 0}
sort.Float64s(fnum)
fmt.Println(fnum)
[2 3 4 6 31 678]
[34 a1a asd hhh]
[-1 0 0 2.2 391.01]
映射
映射,相当于其它语言的hash-map。是key/value对
定义
//只定义不初始化,那么它是一个值为nil的映射
var score map[string]int
fmt.Printf("%T %#v\n", score, score)
if score == nil {
fmt.Println("score == nil")
}
//结果
map[string]int map[string]int(nil)
score == nil
字面量初始化
//使用字面量的形式初始化得到的是空映射
var score2 = map[string]int{}
if score2 != nil {
fmt.Println("score2 != nil")
}
score2 = map[string]int{"小李": 10, "小王": 20, "小张": 30}
fmt.Println(score2)
//使用make方法初始化映射
var score3 = make(map[string]int) //也是个空映射
fmt.Println(score3)
if score3 != nil {
fmt.Println("score3 != nil")
}
//结果
score2 != nil
map[小张:30 小李:10 小王:20]
map[]
score3 != nil
操作-增、删、改、查
查
var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
p := price["大米"]
fmt.Printf("%T %#v %v\n", p, p, p)
//访问不存在的key值
value := price["粉笔"]
fmt.Printf("%T %v \n", value, value)
//如果price中本来就有一个key/value,的值等于0.就会混淆
//所以
v, ok := price["粉笔"]
fmt.Printf("v:%T %v ; ok:%T %v\n", v, v, ok, ok)
//通过ok的值来判断key=粉笔是否存在
if ok {
fmt.Println(v)
}
//也可以把v,ok变成局部变量
if v, ok := price["粉笔"]; ok {
fmt.Println(v)
} else {
fmt.Println("粉笔不存在")
}
//结果
int 20 20
int 0
v:int 0 ; ok:bool false
粉笔不存在
改
var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
price["大米"] = 100
fmt.Println(price)
//结果
map[大米:100 白面:30 豆油:40]
增
映射是无序的,先添加的也不一定就是在前面
var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
price["粉笔"] = 10
fmt.Println(price)
//结果
map[大米:20 白面:30 粉笔:10 豆油:40]
删
var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
delete(price, "白面")
fmt.Println(price)
//结果
map[大米:20 豆油:40]
获取映射的长度用 len()
var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
fmt.Println(len(price))
遍历
映射是无序的,遍历出来的顺序和添加时的顺序不一样
var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
for k, v := range price {
fmt.Printf("%v=%v \n", k, v)
}
//结果
白面=30
豆油=40
大米=20
映射是无序的
go的映射是使用hashtable实现的,key和value建立关系是通过计算key,把key转化成一个int索引来和value 一 一映射,但是这个由key计算出的int值是不确定的,以至于映射是无序的
关于key和value的类型
key的类型至少可以进行 == != 的运算
key的类型可以是:bool int string array 不能是slice不能是map
value可以是任意类型,包括slice map
//定义
var user map[string]map[string]string
//初始化
user = map[string]map[string]string{"小胖": {"班级": "3年1班", "年龄": "10岁"}, "吴亦凡": {"班级": "大一", "年龄": "18岁"}}
fmt.Println(user)
//元素的操作-增加
user["美美"] = map[string]string{"班级": "6年一班", "年龄": "13岁"}
fmt.Println(user)
//元素的操作-改
user["美美"]["班级"] = "4年5班"
fmt.Println(user)
//元素的操作-删除
delete(user["美美"], "年龄")
fmt.Println(user)
//结果
map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班]]
map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班] 美美:map[年龄:13岁 班级:6年一班]]
map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班] 美美:map[年龄:13岁 班级:4年5班]]
map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班] 美美:map[班级:4年5班]]
映射的练习
计数
var stack = []string{"钢笔", "钢笔", "铅笔", "钢笔", "毛笔", "毛笔", "钢笔", "毛笔", "毛笔"}
var count = map[string]int{}
for _, v := range stack {
// if _, ok := count[v]; !ok {
// count[v] = 1
// } else {
// count[v] += 1
// }
//优化后
count[v] += 1
}
fmt.Println(count)
//结果
map[毛笔:4 钢笔:4 铅笔:1]
统计每个英文字母出现的次数
var char = `I say to you today, my friends.
And so even though we face the difficulties of today and tomorrow, I still have a dream. It is a dream deeply rooted in the American dream.
I have a dream today!`
var arr = map[rune]int{}
for _, v := range char {
if v >= 'A' && v <= 'Z' || v >= 'a' && v <= 'z' {
arr[v] += 1
}
}
fmt.Printf("%#v\n", arr)
fmt.Println("------------------------------------------------")
for key, cnt := range arr {
fmt.Printf("%c : %v\n", key, cnt)
}
//结果
map[int32]int{65:2, 73:4, 97:16, 99:3, 100:13, 101:18, 102:5, 103:1, 104:6, 105:8, 108:4, 109:7, 110:6, 111:13, 112:1, 114:9, 115:6, 116:12, 117:3, 118:3, 119:2, 121:7}
------------------------------------------------
A : 2
v : 3
l : 4
I : 4
a : 16
t : 12
d : 13
m : 7
f : 5
r : 9
n : 6
g : 1
s : 6
y : 7
o : 13
u : 3
i : 8
c : 3
e : 18
h : 6
w : 2
p : 1