前言
前面学习了Go数据类型中的数字、字符串,下面看看Go中是否有Python中的list、dict等复合数据类型?
需要注意的是在Go中数组是可变类型+值(拷贝)类型,而go中切片是引用类型;
数组声明之后它里面的元素是可以被修改,如果把1个数组变量引用到1个新的变量,那么这个新的变量会重新拷贝一份旧数组的数组。
Python中的list是可变+引用数据类型
llist1 = ["A", "B", "C"]
llist2=llist1
llist1[0]= "a"llist2[2] = "b"
print(llist1, id(llist1))print(llist2, id(llist2))"""['a', 'B', 'b'] 42775624
['a', 'B', 'b'] 42775624"""
Python的list可变+引用
Golang中的arry是可变+拷贝数据类型
package main
import"fmt"func main() {
arry1 := [3]string{"A", "B", "C"} //注意声明arry变量时不要忘记[指定长度],否则声明的是slice变量
arry2 :=arry1
arry1[0] = "a"arry2[1] = "b"
//说明go中的arry不是引用类型,单属于可变数控类型可以原处修改
fmt.Printf("%v %p\n", arry1, &arry1)
fmt.Printf("%v %p\n", arry2, &arry2)
}/*[a B C] 0xc000062330
[A b C] 0xc000062360*/
Golang的arry可变+拷贝
Python的list切片之后会开辟1开新的内存生产1个新的list数据类型
nameList=["张","根"]
firstName=nameList[0]
lastName=nameList[1]print(firstName)print(lastName)#Python的list切片会开辟1块新的内存,创建1个新的列表。
nameSlice=nameList[0:2]
nameSlice[0]="Martin"nameSlice[1]="Zhang"
print(nameList,nameSlice)print(type(nameList),type(nameSlice))print(id(nameList),id(nameSlice))'''张
根
['张', '根'] ['Martin', 'Zhang']
42632648 42632712'''
Python的list切片
Golang的arry切片之后会开辟1个新的内存生成1个slice数据类型
package main
import ("fmt"
"reflect")
func main() {
nameArry:=[2]string{"张","根"}//1.获取数组的长度
fmt.Println(len(nameArry))//2.通过数组索引取值
firstName:=nameArry[0]
lastName:=nameArry[1]
fmt.Println(firstName)
fmt.Println(lastName)//3.数组组切片
nameSlice:=nameArry[0:2]
nameSlice[0]="Martin"nameSlice[1]="Zhange"fmt.Println(nameArry,nameSlice)//[Martin Zhange] [Martin Zhange]
fmt.Println(reflect.TypeOf(nameArry),reflect.TypeOf(nameSlice)) //[2]string(数组类型)-------- []string(切片类型)
fmt.Printf("切片之前的内存地址:%p,切片之后的slice内存地址:%p--",&nameArry,nameSlice)
}
Golang的Arry切片
package main
import "fmt"
func main() {
//声明1个字符串变量
var name string
fmt.Println(name)
//声明1个数组变量
var a1 [3]int
fmt.Println(a1)
//声明1个切片类型的变量
var s1 []int
fmt.Println(s1 == nil)
//声明二维数组
var a3 [3][3]int
//对二维数组进行初始化
a3 = [3][3]int{
[3]int{1, 2, 3},
[3]int{4, 5, 6},
[3]int{7, 8, 8},
}
fmt.Println(a3)
//
a4 := [3]int{1, 2, 3}
a5 := modifyArry(a4)
fmt.Println(a4)
fmt.Println(a5)
s2 := []string{"河北", "保定", "唐县"}
s3 := modifySlice(s2)
fmt.Println(s2)
fmt.Println(s3)
}
//数组是值类型:修改数组时,会复制 1个新的数组让我们修改
func modifyArry(a1 [3]int) (ret [3]int) {
a1[0] = 100
return a1
}
//切片是引用类型:修改切片时,不会复制1个新的切片让我们修改(修改原来的)
func modifySlice(s2 []string) (ret2 []string) {
s2[0] = "河北省"
return s2
}
数组
数组就是存放同1种数据基本数据类型(数值,字符、字符串、布尔值)元素的容器。
Go中的数组有一下特点:
在声明arry变量时就要声明arry的长度、容器内存放元素的数据类型;
数组的长度是数据类型的1部分,所以2个长度不同arrr,即使他们都属于数组,但2者的数据类型完全不一致,所以2者无法相互比较和赋值。
数组如果没有初始化里面的元素默认为0值(bool=false,int or float=0, string=空字符串)
数组的声明和初始化
package main
import "fmt"
func main() {
//数组:存放同1中基础数据类型的容器
//声明1个长度为3的数组,里面存放bool数据类型
var arry1 [3]bool
var arry2 [4]bool
var arry3 [3]bool
fmt.Printf("arry1:%T arry2:%T arry3:%T\n", arry1, arry2, arry3)
fmt.Println(arry1 == arry3)
//初始化方式1:
arry1 = [3]bool{true, true, true}
fmt.Println(arry1) //[true true true]
//初始化方式2:[...]自动推算数组的长度
arry4 := [...]int{1, 2, 3, 4}
fmt.Println(arry4)
//初始化方式3:利用默认值
arry5 := [5]int{12, 13}
fmt.Println(arry5) //[12 13 0 0 0]
//初始化方式4:根据索引指定arry中的元素
arry6 := [5]int{0: 1, 4: 6}
fmt.Println(arry6)//[1 0 0 0 6]
}
遍历数组
package main
import ("fmt"
"reflect")
func main() {
nameArry := [2]string{"张", "根"}//1.获取数组的长度
fmt.Println(len(nameArry))//2.通过数组索引取值
firstName := nameArry[0]
lastName := nameArry[1]
fmt.Println(firstName)
fmt.Println(lastName)//3.数组组切片
nameSlice := nameArry[0:2]
nameSlice[0] = "Martin"nameSlice[1] = "Zhange"fmt.Println(nameArry, nameSlice)//[Martin Zhange] [Martin Zhange]
fmt.Println(reflect.TypeOf(nameArry), reflect.TypeOf(nameSlice)) //[2]string(数组类型)-------- []string(切片类型)
fmt.Printf("切片之前的内存地址:%p,切片之后的slice内存地址:%p--\n", &nameArry, nameSlice)//2.循环数组
fmt.Println(nameArry)for i := 0; i < len(nameArry); i++{
fmt.Println(i, nameArry[i])
}for item :=range nameArry {
fmt.Println(item)
}for index :=range nameArry {
fmt.Println(index)
}for index, item :=range nameArry {
fmt.Println(index, item)
}
}
遍历数组
多维数组
多维数组就是数组里面嵌套数组
package main
import (
"fmt"
)
func main() {
//定义多维数组
a1 := [3][2]int{{1, 2}, {1, 3}, {1, 4}}
fmt.Println(a1) //[[1 2] [1 3] [1 4]]
//遍历多维数组
for _, v := range a1 {
for _, v1 := range v {
fmt.Println(v1)
}
}
}
数组的内存管理机制
Golang字符串类型存储的机制
我们在Golang中声明的字符串类型变量,编译器不会直接存储该字符串的值。
而是先开辟1块内存把字符串的值存储起来,然后开辟1块内存把这个字符串的指针(8个字节)和长度(8个字节)存储起来赋予变量。
为什么要这样存储1个字符串呢?额外开辟1块存储指针和长度的内存空间。
我感觉这是是为了字符串被放到到Arry或Slice之后的查询效率而做的铺垫。
1.数组的内存地址是连续的
2.数组的内存地址=数组中第1个元素的内存地址
这种设计可以让我们快速的从1个元素获取到数组中最后1个元素,查询效率大大提升。
既然声明1个数组即开辟了1块连续的内存,对Arry中的item进行顺序存储,那么Arry的item含有字符串呢?
字符串可能是英文/汉字它们占用内存的规格可不一致啊!既然如此那我就同一一下规格!
3.声明1个字符串数组时,开启一段连续的内存之后,(并不会在这段连续的内存上直接存储字符串的值,而是存储字符串值的指针(占4个字节)和长度(占4字节)=16个字节。这也是Golang中字符串数据类型的存储机制,连续的内存+指针=加快查询效率...)
这样Arry中每1个元素对这段连续内存的使用都有了固定的规格,节约了Arry对内存空间占用。
随意字符串存储时增加了额外的空间去存储指针和长度,但是加速了查询效率。用空间换时间吧!
package main
import"fmt"
//1.数组的内存是连续的//2.因为数组的内存是连续的,所以数组的内存地址就是数组里第1个元素的内存地址。
func main() {
numbers := [3]int8{11, 22, 33}
fmt.Printf("%p\n", &numbers) //0xc00000a0a8
fmt.Println(numbers) //[11 22 33]
fmt.Printf("%p\n", &numbers[0]) //0xc00000a0a8
fmt.Printf("%p\n", &numbers[1]) //0xc00000a0a9
fmt.Printf("%p\n", &numbers[2]) //0xc00000a0aa
numbers32 := [3]int32{11, 22, 33}
fmt.Printf("%p\n", &numbers32) //0xc00011e0b4
fmt.Println(numbers) //[11 22 33]
fmt.Printf("%p\n", &numbers32[0]) //0xc00011e0b4
fmt.Printf("%p\n", &numbers32[1]) //0xc00011e0b8
fmt.Printf("%p\n", &numbers32[2]) //0xc00011e0bc
var names = [2]string{"张根", "Martin"}
fmt.Printf("%p\n", &names) //0xc0000044c0
fmt.Println(names) //[张根 Martin]
fmt.Printf("%p\n", &names[0]) //0xc000114460
fmt.Printf("%p\n", &names[1]) //0xc000114470
}
Arry内存管理机制
Go中多维数组是数值类型不是引用类型
值类型:就是重新开辟新的内存,与引用类型相反
package main
import (
"fmt"
)
func main() {
//arry数值类型辩证
a1 := [...]string{"河北", "河南", "山东"}
a2 := a1
a1[len(a1)-1] = "山西"
fmt.Println(a1) //[河北 河南 山西]
fmt.Println(a2) //[河北 河南 山东]
}
练习题
package main
import (
"fmt"
)
func main() {
//1.求数组[1, 3, 5, 7, 8]所有元素的和
arr1 := [...]int{1, 3, 5, 7, 8}
var sum int
for _, v := range arr1 {
sum += v
}
fmt.Println(sum)
/*找出数组中和为指定值的两个元素的下标,比如从数组[1, 3, 5, 7, 8]中找出和为8的两个元素的下标分别为(0,3)和(1,2)*/
for i := 0; i < len(arr1); i++ {
for j := i + 1; j < len(arr1); j++ {
if arr1[i]+arr1[j] == 8 {
fmt.Printf("%d %d\n", i, j)
}
}
}
}
package main
import"fmt"
/*1.Go语言的int占多少个字节?
答:取决于你的操作系统 32位 4个字节,64位8个字节
2.整型中有符号和无符号是什么意思?
答:所谓有符号是使用1个位表示征服性质,因为使用第1位表示正负性质,所以有符号整型可表示的正数范围比较小,无符号整型表示的整数范围比较大!
3.整型可以表示的最大范围是
答:0-2**64,超出之后使用bigint包
4.什么是nil
答:nil就是空的数据
5.十进制是以整型的方式存在,其他其他进制以字符串的形式存在?如何实现进制之间的转换?
答:在golang中10进制是以整型存在,其他进制(二进制、八进制、十六进制)都是以字符串的形式存在
bynaryNumber:=strconv.FormatInt(int64(29),2)//十进制转二进制
octalNumber:=strconv.FormatInt(int64(29),8)//十进制转八进制
hexaDecimal:=strconv.FormatInt(int64(29),16)//十进制转八进制
fmt.Println(bynaryNumber,octalNumber,hexaDecimal)
fmt.Println(strconv.ParseInt(hexaDecimal,16,32))
6.简述如下代码的意义:var v1 int|var v2 *int var v3=new(int)
var v1 int //开辟1块个内存+内存初始化值位0,v1指向该内存地址,打印显示0
fmt.Println(v1)
v2:=999 //开辟1块个内存+内存初始化值设置为999,v2指向该内存地址,打印显示0
fmt.Println(v2)
var v3 *int
fmt.Printf("编译器开辟1块名为:%p的内存 存储了1个%p的空指针,空指针指向了nil(并没有初始化)\n",&v3,v3)
v4:=new(int)
fmt.Printf("编译器开辟1块名为:%p的内存 存储了1个指针%p,指针指向%p内存这块内存上存了值%d\n",&v4,v4,&*v4,*v4)
7.浮点型为什么无法精准表示小数?
答:这取决于 浮点型的小数部。小数部分 分转换成二进制时得不出1就这个浮点型就无法精确表达。
8.如何使用第3方包decimal?
答:
var v1 =decimal.NewFromFloat(0.0000019)
var v2 =decimal.NewFromFloat(0.298766)
var v3 =v1.Add(v2)
var v4 =v3.Sub(v2)
var v5 =v4.Mul(v2)
var v6 =v4.Div(v1)
fmt.Println(v3)
fmt.Println(v4)
fmt.Println(v5)
fmt.Println(v6)
var price=decimal.NewFromFloat(3.6615926)
fmt.Println(price.Round(1)) //保留小数点后1位自动四舍五入
fmt.Println(price.Truncate(2))//保留小数点后2位不需要四舍五入
9.简述ascii、Unicode、utf-8的关系
答:
American Standard Code for Information Interchange
是存储了欧美国家在计算机中可以用到字符和对应二进制的字符集,这些二进制使用8位表示足够
Unicode:描述全球人使用字符和对应码位的字符集 使用32位表示码位范围。
utf-8是对unicode的码位进行画区间然后根据字符的码位区间进行模板选择完成Unicode码位和模板的填充
10.判断Go语言中的字符是utf-8编码的字节
name:="张根"
fmt.Println(strconv.FormatInt(int64(name[0]),2))
fmt.Println(strconv.FormatInt(int64(name[1]),2))
fmt.Println(strconv.FormatInt(int64(name[2]),2))
//11100101 10111100 10100000 去对应utf-8的模板一看便知
11.什么是rune?
rune表示英文字符或者汉字在Unicode字符中对应的码位
name:="张根"
EnglishName:="Martin"
fmt.Println([]rune(name))
fmt.Println([]rune(EnglishName))
[24352 26681]
[77 97 114 116 105 110]
12.判断:字符串是否可变?
答:不可变
13.列举你记得的字符串常见功能
答:Prefix() stufix()
14.字符串集合和rune集合如何实现相互转换
name:="Alex"
newRune:=[]rune(name)
newRune[0]='a'
fmt.Println(string(newRune))
15.如何实现字符串高效率拼接
var builder strings.Builder
builder.WriteString("我爱")
builder.WriteString("北")
builder.WriteString("京")
result11:=builder.String()
fmt.Println(result11)
16.简述数组的存储原理
答:一块连续的内存地址,第一个元素即该数组的内存地址。如果数组内包含字符串 存储字符串(长度+指针)进行统一规格!
17.names:=[3]string{"alex","超级无敌小JJ","傻儿子"}
a.根据索引获取“傻儿子”
b.根据索引获取“alex”
c.根据索引获取“小JJ”
d.请将names数组最后1个元素修改为“大烧瓶”
答:
names := [3]string{"alex", "超级无敌小JJ", "傻儿子"}
fmt.Println(names[0])
fmt.Println(names[1])
fmt.Println(names[2])
names[0]="大烧饼"
fmt.Println(names)
18.看代码输出结果
var nestdata [3][2]int
fmt.Println(nestdata)
[[0 0] [0 0] [0 0]]
19.请声明1个3个元素的数组,元素的类型是数组,并在数组中初始化值
var nestdata=[3][2]int{{1,2},{3,4},{5,6}}
fmt.Println(nestdata)
20循环下列数组并使用字符串格式化输出一下内容:
dataList:=[
["alex","qwe123"],
["eric","qw2"],
["tony","qpep23"],
]
最终实现:
我是Alex我的密码是qwe123
答:
var userList = [3][2]string{{"alex", "qwe123"}, {"eric", "qw2"}, {"tony", "qpep23"}}
fmt.Println(userList)
for _, item := range userList {
fmt.Printf("我是%s我的秘密是%s\n", item[0], item[1])
}
21.实现用户登录
//userList中有3个用户,
dataList:=[
["alex","qwe123"],
["eric","qw2"],
["tony","qpep23"],
]
从dataList中获取用户名验证用户合法性*/func main() {var userList = [3][2]string{{"alex", "qwe123"}, {"eric", "qw2"}, {"tony", "qpep23"}}var userName string
var passWord stringfmt.Print("请输入用户名:")
fmt.Scan(&userName)
fmt.Print("请输入秘密:")
fmt.Scan(&passWord)
isAuthenticated := 0
if len(userName) > 0 && len(passWord) > 0{
isAuthenticated= 197
for _, item :=range userList {if userName == item[0] {//当用户名和密码全部正确是也会走这个if分支!
isAuthenticated = 198}if passWord == item[1] {//当用户名和密码全部正确是也会走 这个if分支!
isAuthenticated = 199}if userName == item[0] && passWord == item[1] {
isAuthenticated= 200
break}
}
}switchisAuthenticated {case 200:
fmt.Println("登录成功!")case 197:
fmt.Println("用户和密码都不对!")case 198:
fmt.Println("用户对了,密码不对!")case 199:
fmt.Println("密码对了,用户名不对!")default:
fmt.Println("用户和密码都没有输入!")
}
}
Doing more practice