Go语言基础(四)
一、运算符
二、复合数据类型,数组
1、数组的声明
2、数组的初始化
3、数组的遍历
4、多维数组
5、数组是值类型
三、切片
1、切片的定义
2、由数组得到切片
3、使用make()函数构造切片
4、切片不能直接比较
5、切片的遍历
6、append为切片追加元素
7、复制切片
8、从切片中删除元素
9、切片排序
Go语言中有5中运算符:算数运算符、关系运算符、逻辑运算符、位运算符、赋值运算符。
1、算数运算符
注意:自增++,自减--,在Go语言中是单独的语句,并不是运算符。
2、关系运算符
Go语言是强类型,相同类型的变量才能比较。
3、逻辑运算符
4、位运算符
位运算符对整数在内存中的二进制位进行操作。
package main
import "fmt"
func main(){
// 位运算,针对的是二进制数
// 5的二进制表示:101
// 2的二进制表示: 10
// &:按位与,两位均为1才为1
fmt.Println(5 & 2) // 000
// |:按位或,两位有一个为1就为1
fmt.Println(5 | 2) // 111 -> 7
// ^:按位异或,两位不一样则为1
fmt.Println(5 ^ 2) // 111 -> 7
// <<左移n位就是乘以2的n次方
fmt.Println(5 << 1) // 101 -> 1010 -> 10
// >>右移n位就是除以2的n次方
fmt.Println(5 >> 1) // 101 -> 10 -> 2
}
5、赋值运算符
1、数组的声明
数组是同一种数据类型元素的集合。在Go语言中,数组在声明时就要先确定大小,且不可改变大小,但可以修改数组成员。
package main
import "fmt"
func main(){
var a1 [3]bool
var a2 [4]bool
fmt.Printf("a1:%Tn a2:%T", a1, a2)
}
// a1:[3]bool a2:[4]bool
// 数组的长度是数组类型的一部分
2、数组的初始化
如果不进行初始化,默认元素都是0值(布尔型:flase,整型和浮点型都是0,字符串为""),初始化有三种方式:
package main
import "fmt"
func main(){
// 初始化方式1
a1 := [3]bool{
true, false, true}
fmt.Println(a1)
// 初始化方式2
a2 := [...]int{
1, 2, 3, 4, 5} // ...表示根据初始值推断数组长度
fmt.Println(a2)
// 初始化方式3
a3 := [5]int{
1, 2} // 其余的默认为0
fmt.Println(a3)
// 如果希望长度为5的数组中间都为0呢?
a4 := [5]int{
0:1, 4:2} // 给初始值加一个索引
fmt.Println(a4)
}
3、数组的遍历
package main
import "fmt"
func main(){
city := [...]string{
"北京", "上海", "广州", "深圳"}
// 1、根据索引遍历
for i:=0; i < len(city); i++ {
fmt.Println(city[i])
}
// 2、for range遍历
for i, v := range city {
fmt.Println(i, v)
}
}
4、多维数组
package main
import "fmt"
func main(){
var a1 [3][2]int
a1 = [3][2]int{
[2]int{
1, 2},
[2]int{
3, 4},
[2]int{
5, 6},
}
fmt.Println(a1)
// 多维数组的遍历
for _, v1 := range a1 {
fmt.Println(v1)
for _, v2 := range v1 {
fmt.Println(v2)
}
}
}
5、数组是值类型
赋值或者传参会复制整个数组,因此改变副本的值不会影响本身的值。
package main
import "fmt"
func main(){
b1 := [3]int{
1, 2, 3}
b2 := b1
b2[0] = 100
fmt.Println(b1, b2)
}
注意:数组支持“==”,“!=”操作符,因为内存被初始化过。
练习题:
1、求数组[1, 3, 5, 7, 8]所有元素的和
2、找出数组中和为指定值得两个元素的上下标,比如从数组[1, 3, 5, 7, 8]中找出和为8的两个元素的上下标分别为(0, 3)和(1, 2)。
package main
import "fmt"
func main(){
// 第一题:
a1 := [...]int{
1, 3, 5, 7, 8}
sum := 0
for _, v := range a1 {
sum = sum + v
}
fmt.Println(sum)
// 第二题:
for i:=0; i < len(a1); i++ {
for j:=i+1; j < len(a1); j++ {
if a1[i] + a1[j] == 8{
fmt.Printf("%d %dn", i, j)
}
}
}
}
因为数组的长度固定并且数组的长度属于类型的一部分,所以数组有很多局限性。比如
// 声明切片类型
a := [3]int{
1, 2, 3}
数组a中有三个元素,就不能再继续往数组a中添加元素了。
1、切片的定义
切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装。切片是一个引用类型,它的内部结构包含地址、长度和容量。切片拥有自己的长度和容量,可以通过len()函数求长度,cap()函数求切片的容量。
package main
import "fmt"
func main(){
// 切片的定义
var s1 []int // 定义一个存放int类型的切片
var s2 []string // 定义一个存放string类型的切片
fmt.Println(s1, s2)
// 初始化
s1 = []int{
1, 2, 3}
s2 = []string{
"北京", "上海"}
fmt.Println(s1, s2)
// 求切片的长度和容量
fmt.Printf("len(s1):%d cap(s1):%d", len(s1), cap(s1))
fmt.Printf("len(s2):%d cap(s2):%d", len(s2), cap(s2))
}
2、由数组得到切片
package main
import "fmt"
func main(){
a1 := [...]int{
1, 3, 5, 7, 9, 11, 13}
s1 := a1[0:4] // [1, 3, 5, 7]
fmt.Println(s1)
s2 := a1[1:6] // [3, 5, 7, 9, 11]
fmt.Println(s2)
s3 := a1[1:]
s4 := a1[:4]
s5 := a1[:]
fmt.Println(s3, s4, s5)
// 切片的容量是指底层数组的容量
fmt.Printf("len(s3):%d cap(s3):%d", len(s3), cap(s3))
}
切片指向了一个底层数组,切片的长度就是它的元素个数,切片的容量就是底层数组从切片的第一个元素到最后一个元素的数量。
package main
import "fmt"
func main(){
// 切片是引用类型
a1 := [...]int{
1, 3, 5, 7, 9, 11, 13}
s1 := a1[0:4] // [1, 3, 5, 7]
a1[1] = 100
fmt.Println(s1)
}
3、使用make()函数构造切片
package main
import "fmt"
func main(){
s1 := make([]int, 5, 10) // 如果不指定容量默认与长度一致
fmt.Printf("s1=%v len(s1):%d cap(s1):%d", s1, len(s1), cap(s1))
}
// s1=[0 0 0 0 0] len(s1):5 cap(s1):10
4、切片不能直接比较
切片不能用“==”操作符来判断两个切片是否有全部相等的元素,切片唯一合法的比较操作是和nil比较。一个nil值的切片是没有底层数组,并且它的长度和容量都是0。但是不能说一个长度和容量都是0的切片一定是nil。因此判断一个切片是否为空,用len(s)==0来判断,不应该使用s == nil来判断。
var s1 []int // len(s1)=0, cap(s1)=0, s1==nil
s2 := []int{} // len(s2)=0, cap(s2)=0, s2!=nil
s3 := make([]int, 0) // len(s3)=0, cap(s3)=0, s3!=nil
5、切片的遍历
切片的遍历和数组是一致的,支持索引遍历和for range遍历。
package main
import "fmt"
func main(){
s1 := []int{
1, 3, 5}
// 切片遍历
// 1、索引遍历
for i:=0; i < len(s1); i++ {
fmt.Println(s1[i])
}
// 2、for range遍历
for i, v := range s1 {
fmt.Println(i, v)
}
}
6、append为切片追加元素
package main
import "fmt"
func main(){
s1 := []string{
"北京", "上海"}
fmt.Printf("s1=%v len(s1):%d cap(s1):%dn", s1, len(s1), cap(s1))
// 调用append函数必须使用原来的切片变量接收返回值
s1 = append(s1, "广州")
fmt.Printf("s1=%v len(s1):%d cap(s1):%dn", s1, len(s1), cap(s1))
}
// s1=[北京 上海] len(s1):2 cap(s1):2
// s1=[北京 上海 广州] len(s1):3 cap(s1):4
每个切片都会指向一个底层数组,这个数组能容纳一定数量的元素,当底层数组不能容纳新的元素的时候,切片就会按照一定策略进行扩容,此时,切片指向的底层数组就会发生改变。
扩容策略:
注意:切片扩容还会根据切片中元素类型不同而做不同处理,比如int和string类型的处理方式就不一样。
7、复制切片
package main
import "fmt"
func main(){
a1 := []int{
1, 3, 5}
a2 := a1 // 赋值
var a3 = make([]int, 3, 3)
copy(a3, a1) // copy(目标切片,数据来源切片)
fmt.Println(a1, a2, a3)
// [1 3 5] [1 3 5] [1 3 5]
a1[0] = 100
fmt.Println(a1, a2, a3)
// [100 3 5] [100 3 5] [1 3 5]
}
8、从切片中删除元素
Go语言中并没有删除切片元素的专用方法,但是可以利用切片本身的特性来删除元素。
package main
import "fmt"
func main(){
a1 := []int{
1, 3, 5}
// 将a1中的索引为1的3这个元素删除
a1 = append(a1[:1], a1[2:]...)
fmt.Println(a1)
}
深入理解这个特性:
package main
import "fmt"
func main(){
x1 := [...]int{
1, 3, 5} // 数组
s1 := x1[:] // 切片
fmt.Println(s1, len(s1), cap(s1))
// 1、切片不保存具体的值
// 2、切片对应一个底层数组
// 3、底层数组都是占用一块连续内存
s1 = append(s1[:1], s1[2:]...) // 修改了底层数组
fmt.Println(s1, len(s1), cap(s1))
// [1 3 5]?
fmt.Println(x1)
}
9、切片排序
package main
import "fmt"
import "sort"
func main(){
var a = [...]int{
3, 7, 8, 9, 1}
sort.Ints(a[:])
fmt.Println(a)
}
我是尾巴~
每日一句毒鸡汤:嘴里整天说着学习是一件如何如何痛苦的事情,但内心却觉得没有比学习更惬意的事情,相比应付复杂的人际关系、就业路上压力和艰辛、以及生活中做人做事的方法等,学习是最轻松无压力的事情,因为已经习惯了,毕竟从上学的第一天起,直到今天,一直都是跟考试相伴而来。
本次推荐:
谷歌浏览器插件下载:
Crx4Chrome - Download CRX for Chrome Apps & Extensionswww.crx4chrome.com继续加油~