Go 语言范围(Range)

Posted 知其黑、受其白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go 语言范围(Range)相关的知识,希望对你有一定的参考价值。

阅读目录

Go 语言范围(Range)

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。

在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

for 循环的 range 格式可以对 slice、map、数组、字符串 等进行迭代循环。

格式如下:

for key, value := range oldMap 
    newMap[key] = value

以上代码中的 key 和 value 是可以省略。

如果只想读取 key,格式如下:

for key := range oldMap

或者这样:

for key, _ := range oldMap

如果只想读取 value,格式如下:

for _, value := range oldMap

range 关键字在 go 语言中是相当常用好用的语法糖,可以用在 for 循环中迭代 array、slice、map、channel、字符串所有涉及到遍历输出的东西。

切片迭代

nums := []int1, 2, 3
for k, v := range nums 
	fmt.Printf("key: %v , value: %v  \\n", k, v)

key: 0 , value: 1
key: 1 , value: 2
key: 2 , value: 3

map迭代起始节点就随机了

kvs := map[string]string
	"a": "a",
	"b": "b",

for k, v := range kvs 
	fmt.Printf("key: %v , value: %v  \\n", k, v)

PS E:\\go_test> go run .\\main.go
key: a , value: a
key: b , value: b
PS E:\\go_test>

字符串迭代

for k, v := range "hello" 
	//注意这里单个字符输出的是ASCII码,用 %c 代表输出字符
	fmt.Printf("key: %v , value: %c\\n", k, v)

PS E:\\go_test> go run .\\main.go
key: 0 , value: h
key: 1 , value: e
key: 2 , value: l
key: 3 , value: l
key: 4 , value: o
PS E:\\go_test>

channel

ch := make(chan int, 10)
ch <- 11
ch <- 12
// 不用的时候记得关掉,不关掉又没有另一个goroutine存在会死锁哦,可以注释掉这一句体验死锁
close(ch)

for x := range ch 
	fmt.Println(x)

PS E:\\go_test> go run .\\main.go
11
12
PS E:\\go_test>

结构体

package main

import (
	"fmt"
	"reflect"
)

type Student struct 
	name string
	age  int


func main() 
	v := reflect.ValueOf(Student"乔峰", 29)
	count := v.NumField()
	for i := 0; i < count; i++ 
		f := v.Field(i) //字段值
		switch f.Kind() 
		case reflect.String:
			fmt.Println(f.String())
		case reflect.Int:
			fmt.Println(f.Int())
		
	

PS E:\\go_test> go run .\\main.go
乔峰
29
PS E:\\go_test>
package main

import (
	"fmt"
	"reflect"
)

type LanType struct 
	s1, s2, s3 string


var language interface = LanType"php", "Go", "Python3"

func main() 
	value := reflect.ValueOf(language)
	for i := 0; i < value.NumField(); i++ 
		fmt.Printf("字段索引 %d: %v\\n", i, value.Field(i))
	

PS E:\\go_test> go run .\\main.go
字段索引 0: Php
字段索引 1: Go
字段索引 2: Python3
PS E:\\go_test>

golang中数组与切片的区别详析

  • 声明方式不同,数组需要指定大小,而切片不用
  • 数组是值传递,切片是引用传递

Go切片和Go数组定义

Go 切片

又称动态数组,它实际是基于数组类型做的一层封装。

Go 数组

数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从 0 开始的下标索引访问元素值。

在初始化后长度是固定的,无法修改其长度。

当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数 len(array) 获取其长度。

切片与数组的区别

  • 数组的零值是元素类型的零值,切片的零值是nil;
  • 数组是固定长度,切片是可变的长度。
  • 数组是值类型,切片是引用类型。

1、 Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。

因此,在 Go 中如果将数组作为函数的参数传递的话,那效率就肯定没有传递指针高了。

2、数组的长度也是类型的一部分,这就说明 [10]int[20]int 不是同一种数据类型。

并且Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限制带来不少局限性。

3、切片(slice)是一个拥有相同类型元素的可变长序列,可以方便地进行扩容和传递,实际使用时比数组更加灵活,这也正是切片存在的意义。

而且切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。

数组

var arr1 [4]int
fmt.Printf("arr1 val:%d arr1 len:%d arr1 cap:%d\\n",
	arr1, len(arr1), cap(arr1))
arr1 val:[0 0 0 0] 
arr1 len:4 
arr1 cap:4
arr := [4]int
fmt.Printf("val:%d len:%d cap:%d\\n", 
arr, len(arr), cap(arr)) 
// val:[0 0 0 0] len:4 cap:4

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
// arr[4] = 5 
// 报错:invalid array index 4
        (out of bounds for 4-element array)
        
fmt.Printf("val:%d len:%d cap:%d\\n", 
arr, len(arr), cap(arr)) 
// val:[1 2 3 4] len:4 cap:4

ss := arr[:]
ssPtr := (*reflect.SliceHeader)(unsafe.Pointer(&ss)).Data
fmt.Printf("ss val:%d len:%d cap:%d ptr:%v\\n",
	ss, len(ss), cap(ss), ssPtr)

/*
ss val:[1 2 3 4] 
len:4 
cap:4 
ptr:824633803136
*/

ss2 := arr[:]
ss2Ptr := (*reflect.SliceHeader)(unsafe.Pointer(&ss2)).Data
fmt.Printf("ss2 val:%d len:%d cap:%d ptr:%v\\n",
	ss2, len(ss2), cap(ss2), ss2Ptr)

/*
ss2 val:[1 2 3 4] 
len:4 
cap:4 
ptr:824633803136
*/

切片

注意:切片的容量可以根据元素的个数的增多自动扩容,但是不会根据元素的个数的减少自动缩容。

var s []int
if s == nil 
	fmt.Println("nil")

fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))
/*
s val:[] 
len:0 
cap:0
*/

s = append(s, 1)
fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))
/*
s val:[1] 
len:1 
cap:1
*/

s = append(s, 1)
s = append(s, 2)
fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))
/*
s val:[1 2] 
len:2 
cap:2
*/

s = append(s, 1)
s = append(s, 2)
s = append(s, 3)
fmt.Printf("s val:%d len:%d cap:%d\\n",
	s, len(s), cap(s))

/*
s val:[1 2 3] 
len:3 
cap:4
*/

代码

package main

import "fmt"

func main() 
	fmt.Println("--------------------数组声明与赋值-----------------")
	// 指定数组大小
	var a1 [5]int
	// 自动推断数组大小
	a2 := [...]int1, 2, 3
	fmt.Println("a1=", a1, "a2=", a2)

	// 按索引赋值
	a3 := [...]int2: 2, 4: 4
	fmt.Println("a3=", a3)
	// 按索引赋值
	a4 := [6]int2: 2, 4: 4
	fmt.Println("a4=", a4)

	fmt.Println("-------------------- 切片声明与初始化-----------------")
	// 定义切片
	var b1 []int
	fmt.Println("b1=", b1)

	// 初始化
	b2 := make([]int, 3, 5)
	fmt.Printf("b2=%v,len=%d,cap=%d\\n", b2, len(b2), cap(b2))

	fmt.Println("--------------------值传递与引用传递-----------------")
	a := [4]float6467.7, 89.9, 21, 78
	b := []int2, 3, 5
	fmt.Printf("变量a---地址:%p,类型:%T,数值:%v,长度:%d \\n", &a, a, a, len(a))
	fmt.Printf("变量b---地址:%p,类型:%T,数值:%v,长度:%d \\n", &b, b, b, len(b))

	c := a
	d := b
	fmt.Printf("变量c---地址:%p,类型:%T,数值:%v,长度:%d \\n", &c, c, c, len(c))
	fmt.Printf("变量d---地址:%p,类型:%T,数值:%v,长度:%d \\n", &d, d, d, len(d))

	a[1] = 200
	fmt.Println("a=", a, "c=", c)
	d[0] = 200
	fmt.Println("b=", b, "d=", d)

PS E:\\go_test> go run .\\main.go
--------------------数组声明与赋值-----------------
a1= [0 0 0 0 0] a2= [1 2 3]
a3= [0 0 2 0 4]
a4= [0 0 2 0 4 0]
-------------------- 切片声明与初始化-----------------
b1= []
b2=[0 0 0],len=3,cap=5
--------------------值传递与引用传递-----------------
变量a---地址:0xc0000141a0,类型:[4]float64,数值:[67.7 89.9 21 78],长度:4
变量b---地址:0xc000008090,类型:[]int,数值:[2 3 5],长度:3
变量c---地址:0xc000014200,类型:[4]float64,数值:[67.7 89.9 21 78],长度:4
变量d---地址:0xc0000080d8,类型:[]int,数值:[2 3 5],长度:3
a= [67.7 200 21 78] c= [67.7 89.9 21 78]
b= [200 3 5] d= [200 3 5]
PS E:\\go_test>

数组声明与赋值

// 指定数组大小
var a1 [5]int
// 自动推断数组大小
a2 := [...]int1, 2, 3
fmt.Println("a1=", a1, "a2=", a2)
// a1= [0 0 0 0 0] a2= [1 2 3]

// 按索引赋值
a3 := [...]int2: 2, 4: 4
fmt.Println("a3=", a3)
// a3= [0 0 2 0 4]

// 按索引赋值
a4 := [6]int2: 2, 4: 4
fmt.Println("a4=", a4)
// a4= [0 0 2 0 4 0]

切片声明与初始化

// 定义切片
var b1 []int
fmt.Println("b1=", b1)
// b1= []

// 初始化
b2 := make([]int, 3, 5)
fmt.Printf("b2=%v,len=%d,cap=%d\\n",
	b2, len(b2), cap(b2))
// b2=[0 0 0],len=3,cap=5

值传递与引用传递

a := [4]float6467.7, 89.9, 21, 78
fmt.Printf("变量a---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&a, a, a, len(a))
变量a---地址:0xc000014180,
类型:[4]float64,
数值:[67.7 89.9 21 78],
长度:4
b := []int2, 3, 5
fmt.Printf("变量b---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&b, b, b, len(b))
变量b---地址:0xc000008078,
类型:[]int,
数值:[2 3 5],
长度:3
b := []int2, 3, 5
a := [4]float6467.7, 89.9, 21, 78
c := a
d := b
fmt.Printf("变量c---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&c, c, c, len(c))
变量c---地址:0xc000014180,
类型:[4]float64,
数值:[67.7 89.9 21 78],
长度:4
b := []int2, 3, 5
a := [4]float6467.7, 89.9, 21, 78
// c := a
d := b
fmt.Printf("变量d---地址:%p,类型:%T,数值:%v,长度:%d \\n",
	&d, d, d, len(d))
变量d---地址:0xc000008078,
类型:[]int,
数值:[2 3 5],
长度:3
a := [4]float6467.7, 89.9, 21, 78
c := a
b := []int2, 3, 5
d := b
a[1] = 200
fmt.Println("a=", a, "c=", c)

d[0] = 200
fmt.Println("b=", b, "d=", d)
a= [67.7 200 21 78] c= [67.7 89.9 21 78]
b= [200 3 5] d= [200 3 5]

以上是关于Go 语言范围(Range)的主要内容,如果未能解决你的问题,请参考以下文章

Go 语言范围(Range)

吴裕雄--天生自然--Go 语言学习笔记--Go 语言范围(Range)

第 十五 课 Go 语言范围(Range)

Go语言学习笔记十二: 范围(Range)

Go语言 | 02 for循环及常用函数的使用

Go语言 | 02 for循环及常用函数的使用