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。传参拷贝的是引用头,底层数据共享,函数内修改底层数据会影响外部。
相关推荐
Ivanqhz25 分钟前
层归一化、残差、前馈网络与激活函数简述
服务器·数据库·人工智能·深度学习·算法
Ai-_Man30 分钟前
您您这可以把Dola的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。用AI导出鸭,答案是可以的
开发语言·前端·人工智能·小程序
Java后端的Ai之路32 分钟前
Python进阶探索23 - Python中的Time与Datetime模块
开发语言·数据库·python·datetime·time
沫璃染墨1 小时前
《从零入门Linux系统篇(五十四):线程篇·七——互斥锁底层原理:从原子交换到线程竞争与锁实现》
linux·运维·服务器·开发语言·c++·驱动开发·系统架构
不会就选b1 小时前
算法日常・每日刷题--<动态规划>3
数据结构·算法
Java后端的Ai之路1 小时前
Python进阶探索24_应用案例_linux系统的监控
linux·开发语言·python·应用·探索
程序员清风2 小时前
PydanticAI 实战:用类型安全构建可靠的 Python Agent
开发语言·python·安全
落魄实习生2 小时前
Agent Scope Java 2.x 系列【10】Middleware
java·开发语言·ai
JWASX2 小时前
Java 转 go 学习 - 类型转换
学习·golang
-dzk-2 小时前
【堆】LC 347.前 K 个高频元素
算法·堆