Go语言-2

一、复合数据结构

Go的复合类型包含数组、切片、map、结构体

1. 数组 Array

数组是固定长度、元素类型相同 的连续内存集合。数组长度属于类型的一部分,[3]int[5]int 是两种完全不同的类型。

数组属于值类型,赋值或者传给函数时,会完整复制一份全部元素,数据量大的时候性能开销高,所以业务代码很少直接使用数组,数组更多是切片的底层载体。

go 复制代码
package main

import "fmt"

func main() {
	// 声明,默认零值初始化
	var arr [3]int
	arr[0] = 10
	arr[1] = 20
	arr[2] = 30
	fmt.Println(arr)

	// 简写初始化
	arr2 := [3]string{"Go", "Rust", "C++"}
	fmt.Println(arr2)

	// 自动推导长度
	arr3 := [...]int{1, 2, 3, 4}
	fmt.Printf("arr3类型:%T\n", arr3)
}
  1. 长度一旦定义,不能扩容;
  2. 遍历可以用for或者for range
  3. 值拷贝,函数接收数组参数会复制整个数组,大数组不推荐。

2. 切片 Slice

切片是Go开发中最常用 的数据结构,它并不是数组,而是对底层数组的引用视图。

切片结构包含三部分:指针(指向底层数组)、len(当前有效元素长度)、cap(底层数组容量) 。切片是引用类型

go 复制代码
package main

import "fmt"

func main() {
	// 方式1:直接字面量创建切片
	s := []int{10, 20, 30}
	fmt.Println("len:", len(s), "cap:", cap(s))

	// 方式2:make创建,make(类型, len, cap)
	s2 := make([]int, 2, 5)
	fmt.Println(s2)

	// append追加元素,超过cap自动扩容
	s = append(s, 40, 50)
	fmt.Println(s)

	// 切片截取 s[low:high],左闭右开,新切片和原切片共享底层数组
	s3 := s[1:3]
	s3[0] = 999
	fmt.Println("原切片", s)
}
  1. 切片截取后共享底层数组,修改新切片会影响原切片;
  2. append扩容时,如果容量不足,会开辟新数组,不再共享底层;
  3. nil切片:var s []int,本身没有底层数组,可以直接append;空切片make([]int,0)有底层数组。

3. map

map是键值对(key-value)集合,适合根据key快速查找数据。map是引用类型

map的key必须是可比较的类型(int、string等,不能是切片、map、函数);key唯一,重复赋值会覆盖旧值;map内部元素无序,遍历顺序每次可能不一样

go 复制代码
package main

import "fmt"

func main() {
	// 声明map,nil,不能直接写入key
	var m map[string]int

	// 必须make初始化
	m = make(map[string]int)
	m["age"] = 22
	m["score"] = 95
	fmt.Println(m["age"])

	// 获取值,同时判断key是否存在
	val, ok := m["name"]
	if ok {
		fmt.Println("name:", val)
	} else {
		fmt.Println("key不存在")
	}

	// 删除key
	delete(m, "age")
}
  1. var m map[xxx]不make,直接写键会panic;
  2. 遍历for range map每次顺序不同;
  3. map的value如果是结构体,不能直接修改结构体内部字段,需要取出或者用指针。

4. 结构体 struct

结构体可以把不同类型的字段 组合在一起,用来描述现实中的实体,比如用户、订单。结构体是自定义类型,可以绑定方法,是Go实现面向对象思想的基础。

go 复制代码
package main

import "fmt"

// 定义结构体
type User struct {
	Name string
	Age  int
}

func main() {
	// 初始化方式1:按顺序赋值
	u1 := User{"lisi", 20}
	// 初始化方式2:指定字段名,推荐,顺序无关
	u2 := User{Name: "wangwu", Age: 21}

	fmt.Println(u1.Name, u1.Age)
	fmt.Println(u2)

	// 修改字段
	u2.Age = 22
	fmt.Println(u2)
}
  1. 字段首字母大写,包外可以访问;小写只能当前包访问;
  2. 结构体可以嵌套结构体;
  3. 结构体本身是值类型,赋值会完整拷贝;结构体指针则不会拷贝。

二、函数

参数、变量、返回值。函数支持多返回值,这是Go最标志性的特性,通常用第二个返回值返回error。

基础函数定义

语法:func 函数名(参数列表) (返回值列表) {函数体}

go 复制代码
package main

import "fmt"

// 普通函数,两个入参,一个返回值
func add(a int, b int) int {
	return a + b
}

// 多返回值,第二个返回error
func div(a, b int) (int, error) {
	if b == 0 {
		return 0, fmt.Errorf("除数不能为0")
	}
	return a / b, nil
}

func main() {
	sum := add(3, 5)
	fmt.Println(sum)

	res, err := div(10, 2)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(res)
	}
}

1. 可变参数

...类型代表可变参数,可以传入任意多个同类型参数,可变参数本质就是切片。可变参数必须放在参数列表最后。

go 复制代码
func sum(nums ...int) int {
	total := 0
	for _, v := range nums {
		total += v
	}
	return total
}

func main() {
	fmt.Println(sum(1, 2, 3))
	fmt.Println(sum(10, 20))
}

2. 匿名函数与闭包

匿名函数:没有函数名,可以直接赋值给变量或者立即执行。

闭包:匿名函数捕获函数外部的变量,闭包保存的是变量引用,不是变量快照。

go 复制代码
package main
import "fmt"

func main() {
	// 匿名函数赋值给变量
	f := func(x int) int {
		return x * 2
	}
	fmt.Println(f(6))

	// 闭包示例
	a := 10
	closure := func() {
		a++
		fmt.Println(a)
	}
	closure()
	closure()
}

3. 方法(绑定到结构体的函数)

方法和普通函数的区别:方法带有接收者,接收者写在函数名前面。分值接收者指针接收者

  • 值接收者:传入结构体副本,方法内部修改不会影响原结构体,开销大,适合小结构体、只读场景。
  • 指针接收者:传入结构体指针,直接操作原结构体,不会拷贝,开发优先使用指针接收者
go 复制代码
package main
import "fmt"

type User struct {
	Name string
}

// 值接收者
func (u User) Hello() {
	fmt.Println("hello,", u.Name)
}

// 指针接收者,可以修改原结构体
func (u *User) Rename(newName string) {
	u.Name = newName
}

func main() {
	u := User{Name: "zhangsan"}
	u.Hello()
	u.Rename("newName")
	fmt.Println(u.Name)
}

4. 函数参数传递规则

  • 值类型:基础类型int、bool、string、数组、结构体。传参是拷贝副本,函数内部修改不影响外部原始变量。
  • 引用类型:切片、map、channel。传参拷贝的是引用头,底层数据共享,函数内修改底层数据会影响外部。
相关推荐
晴天161 小时前
JavaScript 与 Java 垃圾回收机制对比
java·开发语言·javascript
3Q工具箱 - BraggTroy1 小时前
Python + PyInstaller 实战:从零打造 10 个 Windows 桌面小工具(附全部踩坑记录与核心代码)
开发语言·windows·python
计算机编程-吉哥1 小时前
基于机器学习的城市交通拥堵分析与预测平台【计算机毕业设计选题·机器学习·随机森林算法】
hadoop·算法·随机森林·机器学习·课程设计·计算机毕业设计选题·大数据毕业设计选题推荐
Wang's Blog1 小时前
Java框架快速入门: Spring Security+OAuth2之多因子认证逻辑与实体类改造
java·开发语言·spring
传奇开心果编程1 小时前
【Rust入门知识点学与练】第26课:Unsafe Rust
开发语言·学习·rust
bug总结1 小时前
uniapp总结
开发语言·前端·javascript
zyeyeye1 小时前
C++入门:从HelloWorld到命名空间揭秘
c语言·开发语言·c++·算法
牧羊人.3331 小时前
动手学深度学习 04 | Dataset 和 DataLoader、数据增强
人工智能·pytorch·深度学习·算法
励志不掉头发的内向程序员1 小时前
【LibreCAD 2D架构】从 addHistory 到 RS_UndoCycle:LibreCAD 里的两套 History 与撤销机制
开发语言·c++·qt·学习·系统架构