有基础转Go语言学习笔记(2. 基本数据结构篇)

有基础转Go语言学习笔记(2. 基本数据结构篇)

1. 数组和切片(Array & Slice)

在Go语言中,数组(Array)和切片(Slice)是基础且常用的数据结构,它们有相似之处,但也有关键的区别。

数组(Array)

数组是具有固定大小的数据结构,用于存储同类型的元素集合。数组的长度在声明时确定,并且不能改变。

var arr [5]int // 声明一个包含5个整数的数组
arr[0] = 1     // 数组的索引从0开始
特点
  1. 固定长度:数组的长度是其类型的一部分,一旦定义就不能改变。
  2. 值类型:在Go中,数组是值类型,这意味着当它们被赋值给一个新变量或作为参数传递时,将进行整个数组的复制。
  3. 内存分配:数组在编译时分配内存,大小固定。

切片(Slice)

切片是对数组的抽象,提供了更加灵活、强大和方便的接口来处理数据序列。切片本身不存储任何数据,它只是对底层数组的引用。

slice := []int{1, 2, 3, 4, 5} // 创建一个切片
newSlice := slice[1:3]         // 创建一个新的切片,包含元素slice[1], slice[2]
特点
  1. 动态大小:切片的长度是可变的,可以随时添加或删除元素(在其容量范围内或通过重新分配来扩展容量)。
  2. 引用类型:切片是引用类型,当它们被赋值给一个新变量时,两个变量将引用相同的底层数组。
  3. 自动管理容量:切片的容量可以根据需要自动增长。append函数用于添加元素到切片中,如有必要会自动扩容。

遍历

在Go语言中,遍历数组和切片主要有以下几种方法:

1. 使用传统的 for循环

使用传统的 for循环,通过索引来访问每个元素。

slice := []int{1, 2, 3, 4, 5}
for i := 0; i < len(slice); i++ {fmt.Println(slice[i])
}

这种方法在需要索引时非常有用,例如,当你需要修改原始数组或切片时。

3. 使用切片操作

在某些情况下,你可能想要遍历切片的一部分。这时,可以结合切片操作和 for循环。

for _, value := range slice[1:4] {fmt.Println(value)
}

这段代码将遍历切片中的第二个到第四个元素。

2. 使用 range关键字

range提供了一种更简洁的方式来遍历数组和切片。

for index, value := range slice {fmt.Printf("Index: %d, Value: %d\n", index, value)
}

如果你只需要值,可以忽略索引:

for _, value := range slice {fmt.Println(value)
}

同样,如果只需要索引:

for index := range slice {fmt.Println(index)
}

使用 range通常是更为“惯用”的Go代码风格。

4. 使用递归

虽然不常见,但在某些特定场景下,你可能会通过递归的方式遍历数组或切片。

func recursivePrint(slice []int, index int) {if index < len(slice) {fmt.Println(slice[index])recursivePrint(slice, index+1)}
}recursivePrint(slice, 0)

这种方法在处理树形结构或需要特殊遍历逻辑时可能会有用。

传参

在Go语言中,将数组作为参数传递给函数时,需要注意几个关键点:

1. 数组是值类型

数组在Go中是值类型,这意味着当数组作为参数传递给函数时,实际上传递的是数组的副本,而不是它的引用。

func modifyArray(arr [5]int) {arr[0] = 10 // 这只会修改数组的副本
}func main() {array := [5]int{1, 2, 3, 4, 5}modifyArray(array)fmt.Println(array) // 输出: [1 2 3 4 5],原始数组未被修改
}

由于数组是值传递,所以任何在函数内部对数组的修改都不会影响原始数组。

2. 使用指针传递数组

为了在函数内部修改数组本身,你可以传递一个指向数组的指针。

func modifyArray(arr *[5]int) {arr[0] = 10 // 现在这会修改原始数组
}func main() {array := [5]int{1, 2, 3, 4, 5}modifyArray(&array)fmt.Println(array) // 输出: [10 2 3 4 5]
}

这种方式允许你在不复制整个数组的情况下修改数组。

3. 使用切片代替数组

在Go中,切片比数组更常用,因为它们更加灵活且易于使用。当你传递切片到函数时,实际上传递的是切片的引用,这意味着在函数内部对切片的修改会影响原始切片。

func modifySlice(s []int) {s[0] = 10
}func main() {slice := []int{1, 2, 3, 4, 5}modifySlice(slice)fmt.Println(slice) // 输出: [10 2 3 4 5]
}

2. 切片(Slice)

在Go语言中,切片(Slice)是一个灵活、功能强大的数据结构,用于表示可变长的序列。切片是对数组的封装,提供了更高层次的抽象。以下是切片的多种声明方式和使用方法:

声明切片

  1. 使用 make函数

    slice := make([]int, 5) // 创建一个长度和容量都是5的切片
    
  2. 直接声明并初始化

    slice := []int{1, 2, 3, 4, 5} // 创建并初始化切片
    
  3. 声明一个nil切片

    var slice []int // 声明一个nil切片
    
  4. 使用现有数组或切片创建新切片

    array := [5]int{1, 2, 3, 4, 5}
    slice := array[1:4] // 基于数组创建切片,包含元素2、3、4
    

使用切片

  1. 添加元素
    使用 append函数向切片添加元素。如果切片的容量不够,append会自动扩容。

    slice = append(slice, 6) // 添加元素到切片
    
  2. 遍历切片
    使用 for循环和 range关键字遍历切片。

    for index, value := range slice {fmt.Println(index, value)
    }
    
  3. 修改切片元素
    直接通过索引修改。

    slice[0] = 10 // 修改第一个元素
    
  4. 切片截取
    切片可以被进一步切割。

    subSlice := slice[1:3] // 创建一个新的切片,包含原切片的第2个和第3个元素
    
  5. 切片的容量和长度
    使用 len获取切片的长度,cap获取切片的容量。

    length := len(slice)
    capacity := cap(slice)
    

len 与 cap

切片(Slice)是一个非常灵活的数据结构,它提供了对底层数组的引用。理解切片的长度(len)和容量(cap)对于正确地使用切片至关重要。

切片的长度(Length)
  • 定义:切片的长度是切片中当前元素的数量。
  • 获取方式:使用内置的 len函数获取,如 len(slice)
  • 特点
    • 长度反映了切片中元素的实际数量。
    • 长度永远不会超过容量。
    • 可以通过 append函数增加切片的长度(在容量允许的范围内)。
    • 切片长度的修改不会影响底层数组的大小。
切片的容量(Capacity)
  • 定义:切片的容量是从切片的第一个元素开始到其底层数组末尾的元素数量。
  • 获取方式:使用内置的 cap函数获取,如 cap(slice)
  • 特点
    • 容量表示切片可以增长到的最大元素数量,不需要重新分配内存。
    • 当使用 append函数增加切片长度超过其容量时,系统会分配一个新的底层数组,并复制原有元素到新数组中(通常是原容量的两倍),从而增加切片的容量。
    • 切片的容量在创建时确定,之后可以通过重新分配底层数组来改变。
    • 切片操作可以创建新的切片,新切片的容量取决于原切片的容量和切片操作的起始位置。
示例
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:3] // 创建一个新切片,包括数组的第2个和第3个元素fmt.Println(len(slice)) // 输出: 2
fmt.Println(cap(slice)) // 输出: 4

在上述示例中,切片 slice的长度为2(包含两个元素),而其容量为4,因为它从底层数组的第二个元素开始,直到数组的末尾。

func main() {  slice := make([]int, 2, 3) // 初始长度为2,容量为3  fmt.Println("Initial Slice:", slice, "Length:", len(slice), "Capacity:", cap(slice))  // 向切片中添加元素,直到超出其容量  for i := 0; i < 5; i++ {  slice = append(slice, i)  fmt.Println("Updated Slice:", slice, "Length:", len(slice), "Capacity:", cap(slice))  }  
}

在这个示例中,我们首先创建了一个长度为2,容量为3的切片。然后,我们通过循环向切片中添加元素,并在每次追加后打印切片的当前状态,包括其长度和容量。

Initial Slice: [0 0] Length: 2 Capacity: 3
Updated Slice: [0 0 0] Length: 3 Capacity: 3
Updated Slice: [0 0 0 1] Length: 4 Capacity: 6
Updated Slice: [0 0 0 1 2] Length: 5 Capacity: 6
Updated Slice: [0 0 0 1 2 3] Length: 6 Capacity: 6
Updated Slice: [0 0 0 1 2 3 4] Length: 7 Capacity: 12
  1. 初始状态:切片的长度是2,容量是3。
  2. 当切片长度达到容量(即 len等于 cap)时,再向切片添加新元素将导致切片的容量增加(通常翻倍),以便容纳更多的元素。

切片截取

Go语言中的切片截取和Python中的差不多。这样可以从现有切片或数组中生成新的切片。切片截取的基本语法是 slice[low:high],其中 low是起始索引(包含),high是终止索引(不包含)。
在Go语言中,切片的截取是通过指定起始索引和终止索引来完成的,这样可以从现有切片或数组中生成新的切片。切片截取的基本语法是 slice[low:high],其中 low是起始索引(包含),high是终止索引(不包含)。

基本示例
originalSlice := []int{1, 2, 3, 4, 5}// 截取索引1(包含)到索引3(不包含)
subSlice := originalSlice[1:3] // 结果是[2, 3]
示例:省略边界
// 从开始到索引3(不包含)
subSlice1 := originalSlice[:3] // 结果是[1, 2, 3]// 从索引2(包含)到结束
subSlice2 := originalSlice[2:] // 结果是[3, 4, 5]// 完整的切片
subSlice3 := originalSlice[:] // 结果是[1, 2, 3, 4, 5]
注意事项
  • 切片截取是基于原切片的,它们共享相同的底层数组。因此,修改新切片中的元素也会影响原切片。
  • 超出原切片边界的截取将导致运行时错误。
  • 切片截取不会复制数据,它仅仅创建了一个新的切片头,指向原来的底层数组。

深拷贝

深拷贝(deep copy)指的是创建一个新的对象,其内容是对原对象的完整复制。与浅拷贝(shallow copy)不同,深拷贝确保原始数据和复制数据不会共享任何子对象。在Go中,对于基本数据类型,赋值操作本身就是深拷贝。但对于复合数据类型(如结构体、切片、映射等),需要特别处理以实现深拷贝。

对于切片和数组,可以使用 copy函数实现深拷贝:

originalSlice := []int{1, 2, 3}
copiedSlice := make([]int, len(originalSlice))
copy(copiedSlice, originalSlice)

深拷贝 + 截取

使用 copy函数可以实现切片的深拷贝,并且可以结合切片截取来只复制切片的一部分。这种方法可以创建一个新的切片,它包含原始切片指定范围内的元素的副本,与原切片拥有独立的底层数组。

示例:深拷贝切片的一部分

假设我们有一个切片,我们想要复制这个切片的一部分到一个新的切片中:

originalSlice := []int{1, 2, 3, 4, 5}// 定义要复制的切片范围
start := 1 // 起始索引(包含)
end := 4   // 结束索引(不包含)// 创建一个新的切片用于存放复制的元素
// 长度是要复制元素的数量
newSlice := make([]int, end-start)// 使用copy进行深拷贝
copy(newSlice, originalSlice[start:end])// 输出结果
fmt.Println("Original Slice:", originalSlice)
fmt.Println("Copied Slice:", newSlice)

在这个示例中,我们创建了一个长度为 end-start的新切片 newSlice,然后使用 copy函数将 originalSlice中从索引 startend的元素复制到 newSlice中。

注意事项
  • 确保新切片的长度足以容纳要复制的元素。如果新切片太小,copy会只复制适合新切片大小的部分元素。
  • copy函数返回复制的元素数量,可以用于检查是否所有需要的元素都被复制了。
  • 如果 startend索引超出原切片的边界,程序将抛出运行时错误。

3. 映射(Map)

映射(Map)是一种内置的数据结构,提供了基于键值对的存储机制。映射在Go中非常有用,用于存储和检索数据。

定义和初始化

  • 定义一个Map

    var myMap map[string]int
    

    这里定义了一个键为 string类型,值为 int类型的映射。

  • 初始化
    使用 make函数初始化映射:

    myMap = make(map[string]int)
    
  • 初始化带有值的Map

    myMap := map[string]int{"one": 1, "two": 2}
    

增加和修改元素

  • 向映射中添加或修改元素:
    myMap["three"] = 3
    

访问元素

  • 访问映射中的元素:

    value := myMap["three"]
    
  • 访问不存在的键时,将返回值类型的零值。例如,对于 int类型,零值是 0

检查元素是否存在

  • 使用两个值来接收返回结果,其中第二个值是一个布尔值,表示键是否存在于映射中:
    value, ok := myMap["three"]
    

删除元素

  • 使用内置的 delete函数删除元素:
    delete(myMap, "three")
    

遍历Map

  • 使用 for循环和 range关键字遍历映射:
    for key, value := range myMap {fmt.Println("Key:", key, "Value:", value)
    }
    

注意事项

  • 映射在Go中是引用类型,意味着当它们被赋值给新的变量或作为参数传递时,新变量和原始映射将引用同一个底层数据。
  • 映射不是并发安全的。在多线程环境下使用映射时,需要使用同步机制,如互斥锁。
  • 映射的顺序是不确定的,每次遍历映射的顺序可能不同。

4. 结构体(struct)

结构体(Struct)是一种自定义的数据类型,允许你组合不同类型的数据项。结构体在Go中非常重要,通常用于表示具有多个属性的复杂数据结构。以下是有关Go中结构体的基本知识点:

定义结构体

使用 typestruct关键字定义结构体。

type Person struct {Name stringAge  int
}

这里定义了一个 Person类型的结构体,有两个字段:NameAge

创建结构体实例

可以通过多种方式创建结构体实例。

  • 使用结构体字面量

    person := Person{"Alice", 30}
    
  • 指定字段名称

    person := Person{Name: "Alice", Age: 30}
    
  • 使用 new关键字

    personPtr := new(Person) // 创建一个指向Person类型的指针
    

访问和修改字段

访问或修改结构体的字段使用点(.)操作符。

fmt.Println(person.Name) // 访问
person.Age = 31          // 修改

方法

在Go中,可以给结构体类型定义方法。

func (p Person) SayHello() {fmt.Println("Hello, my name is", p.Name)
}

这里定义了一个 SayHello方法,它绑定到 Person类型。

指针和结构体

可以使用结构体指针来避免在函数调用时复制结构体的数据。

func (p *Person) SetAge(age int) {p.Age = age
}

这个方法使用指针接收器,允许在不复制结构体的情况下修改它。

匿名字段和嵌入结构体

结构体可以包含匿名(或嵌入)字段,这允许一种简单的形式的继承。

type Employee struct {PersonPosition string
}

这里 Employee结构体嵌入了 Person,因此继承了 Person的所有字段。

导出和非导出字段

如果结构体字段的名称以大写字母开头,则该字段被导出(即在包外可见)。以小写字母开头的字段是非导出的,仅在其定义的包内可见。

总结

结构体是Go中处理复杂数据的基石,允许将相关的数据组合在一起。通过使用结构体和其方法,Go语言提供了一种简单而强大的方式来表示和操作复杂的数据结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/200492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

adb和bat的局限性

ADB&#xff08;Android调试桥&#xff09;和BAT&#xff08;批处理文件&#xff09;都是非常有用的工具&#xff0c;但它们也各自存在一些局限性。 ADB的局限性包括&#xff1a; 设备兼容性&#xff1a;某些设备可能由于制造商定制的原因对ADB支持不完善。 需要USB连接&#…

【电路笔记】-电阻器额定功率

电阻器额定功率 文章目录 电阻器额定功率1、概述2、电阻功率&#xff08;P&#xff09;3、功率电阻器4、电阻器额定功率示例15、电阻器额定功率示例2 电能被电阻吸收&#xff0c;因为它是电压和电流的乘积&#xff0c;一些电阻将这种电能转化为热量。 1、概述 当电流由于电阻器…

写在FastAPI之旅之前

在过去的十年里&#xff0c;我深入参与了Java、PHP和Golang的开发工作。从最初使用Java的原生servlet进行web开发&#xff0c;到后来拥抱Spring MVC和Spring Boot&#xff0c;我见证了框架的演进和开发效率的不断提升。然而&#xff0c;当我转而使用PHP的Laravel和Golang的beeg…

flex 布局防止元素被挤换行

刚开始是这样的代码&#xff1a; <div class"flex"><span>选择模型&#xff1a;</span><n-select :options"state.chatModelOptions" /> </div>选择模型换行了…不行&#xff0c;这个效果不行&#xff0c;修改后&#xff1…

windows10系统下替换、修改jar中的文件并重新打包成jar文件然后运行

目录 1、jar文件简述2、问题来源3、操作步骤3.1 解压jar包3.2 替换或者更改操作3.3 重新打成jar包3.4 确认是否修改成功3.5 运行程序 附录&#xff1a;常见命令参数 1、jar文件简述 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的…

哈希表【2】

文章目录 &#x1f348;217. 存在重复元素&#x1f34c;1. 题目&#x1f34f;2. 算法原理&#x1f353;3. 代码实现 &#x1f383;219. 存在重复元素 II&#x1f384;题目&#x1f386;算法原理&#x1f9e8;代码实现 &#x1f348;217. 存在重复元素 &#x1f34c;1. 题目 题…

JFrog----基于Docker方式部署JFrog

文章目录 1 下载镜像2 创建数据挂载目录3 启动 JFrog服务4 浏览器登录5 重置密码6 设置 license7 设置 Base URL8 设置代理9 选择仓库类型10 预览11 查看结果 1 下载镜像 免费版 docker pull docker.bintray.io/jfrog/artifactory-oss体验版&#xff1a; docker pull releas…

LangChain 20 Agents调用google搜索API搜索市场价格 Reason Action:在语言模型中协同推理和行动

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

Debian 终端Shell命令行长路径改为短路径

需要修改bashrc ~/.bashrc先备份一份 cp .bashrc bashrc.backup编辑bashrc vim ~/.bashrc可以看到bashrc内容为 # ~/.bashrc: executed by bash(1) for non-login shells. # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) # for examples# If…

道可云元宇宙每日资讯|潍坊市元宇宙产业发展座谈会召开

道可云元宇宙每日简报&#xff08;2023年12月5日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 元宇宙数字内容创作大赛在璧山区举行 3日下午&#xff0c;梦界元境杯元宇宙数字内容创作大赛、第十四届蓝桥杯大赛专项赛颁奖典礼在重庆市璧山区举行。 本次大赛&am…

微信小程序网络请求二次封装

在微信小程序中&#xff0c;网络请求操作是非常频繁的&#xff0c;这样导致重复的代码太多了&#xff0c;防止浪费一些不必要的时间&#xff0c;所以&#xff0c;在项目搭建之初&#xff0c;请封装好一切可以封装的代码。 首先配置一个基地址&#xff1a; export default {ho…

基于FPGA的数字时钟设计与实现(含源码)

随着数字电子技术的不断发展&#xff0c;基于FPGA&#xff08;现场可编程门阵列&#xff09;的数字时钟设计方案逐渐成为了一种流行的选择。本篇博客将详细介绍如何利用FPGA实现一个简单的数字时钟&#xff0c;涉及到分频器、数码管驱动、时分秒计数、三八译码器和扫描数码管等…

禅道v11.6 基于linux环境下的docker容器搭建的靶场

一、环境搭建 linux环境下的 在docker环境下安装禅道CMS V11.6 docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD123456 -d docker.io/yunwisdom/zentao:v11.6二、常见问题 1.删除…

Swing程序设计详解(二)

一 文件标签组与图标 在Swing程序设计中&#xff0c;标签(JLabel)被用于显示文本、图标等内容。在Swing应用程序的用户系面中&#xff0c;用户能够通过标签上的文本、图标等内容获得相应的提示信息。 1.1 JLable标签 标签(JLabel)的父类是JComponent类。虽然标签不能被添加…

Canal笔记:安装与整合Springboot模式Mysql同步Redis

官方文档 https://github.com/alibaba/canal 使用场景 学习一件东西前&#xff0c;要知道为什么使用它。 1、同步mysql数据到redis 常规情况下&#xff0c;产生数据的方法可能有很多地方&#xff0c;那么就需要在多个地方中&#xff0c;都去做mysql数据同步到redis的处理&…

在微信小程序中如何改变默认打开的页面

在微信小程序中&#xff0c;在我们编写页面的时候&#xff0c;可能会在重新渲染的时候导致页面跳转到默认打开的页面上&#xff0c;为了提升用户的一个体验&#xff0c;我们可以设置一些内容来修改小程序默认打开的页面&#xff0c;提升开发者的开发体验。 当我们打开一个微信…

nodejs微信小程序+python+PHP在线购票系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

某60区块链安全之JOP实战二学习记录

区块链安全 文章目录 区块链安全Jump Oriented Programming实战二实验目的掌握对EVM逆向能力实验环境实验工具实验原理实验内容Jump Oriented Programming实战二 实验步骤Jump Oriented Programming实战二 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约中中Jum…

MyBatis 常见面试题

目录 1.MyBatis——概述1.1.什么是 ORM 框架&#xff1f;1.2.✨谈谈对 MyBatis 的理解。1.3.使用 MyBatis 相对于直接使用 SQL 有哪些优点&#xff1f;1.4.MyBatis 有什么优缺点&#xff1f;1.5.✨MyBatis 的分层结构是什么样的&#xff1f;1.6.✨MyBatis 的执行流程是什么样的…

机器学习实验一:线性回归

系列文章目录 机器学习实验一&#xff1a;线性回归机器学习实验二&#xff1a;决策树模型机器学习实验三&#xff1a;支持向量机模型机器学习实验四&#xff1a;贝叶斯分类器机器学习实验五&#xff1a;集成学习机器学习实验六&#xff1a;聚类 文章目录 系列文章目录一、实验…