学习 Go 语言 1 — 基础语法(二)

简介: 最近在闲来无事之际开始学习点 Go 语言,Go 语言在近几年很火,有 Docker 和 Kubernetes 这两大杀器做支撑,它已经是云计算领域主流的编程语言了,并且有蚕食部分 C、C++、Java 等领域的趋势,怀着一些对新技术的兴趣,我开始学习了 Go 语言,在对其有了简单的了解之后,我渐渐开始喜欢上这门语言了。

三、常用数据结构


3.1 数组

先来看一下 Go 语言中的数组的几种声明方式:

var arr [3]int  //声明数组并初始化为零值
arr[0] = 10
arr2 := [3]int{1, 2}   //声明时初始化
arr3 := make([]int, 2)    //声明长度为2的数组
arr4 := [3][3]int{{1, 2, 3}, {4, 5, 6}}    //多维数组的初始化

数组的常见操作是对其进行遍历,我们可以按照一贯的做法,写一个 for 循环来完成,但是 Go 语言中的 range 关键字可以使代码更加的简洁:

//常规的遍历方式
for i := 0; i < len(arr2); i++ {
   fmt.Println(arr2[i])
}
//数组元素的遍历
for i, v := range arr2 {
   fmt.Println(i, v)
}
//忽略下标或者值,_ 表示
for _, val := range arr2{
   fmt.Println(val)
}
数组还支持截取操作,这和 python 的特性非常类似:
//数组截取
fmt.Println(arr[0:2])
fmt.Println(arr[:2])
fmt.Println(arr[0:])
fmt.Println(arr[1:len(arr)])


3.2 切片

切片可以看做是对数组的一层封装,因为每个切片的底层一定都是一个数组,先来看看切片的声明方式:

var sli0 []int
sli0 = append(sli0, 1, 2)
sli1 := []int{}
sli1 = append(sli1, 1, 2, 3)
sli2 := []int{1, 2, 3}
sli3 := make([]int, 5) //指定长度,默认值0
fmt.Println(len(sli2))
fmt.Println(cap(sli2))
sli4 := make([]int, 5, 8)  //指定长度及容量,
fmt.Println(sli4[3])      //可以访问,默认值0
fmt.Println(sli4[7])      //无法访问
可以看到切片的声明方式其实和数组非常的类似,只是方括号中没有了表示指定长度的数字。如果需要往切片中新增加元素,我们可以使用 append 函数:
sli := make([]int, 5)
sli = append(sli, 1)
sli = append(sli, 2, 3)

那么数组和切片的区别都有哪些呢?

数组的长度一定是一个定值,就是其在声明时所指定的长度,但是切片类型的长度是可变化的,切片的长度随着其中元素的增长而增长,只不过却不会随着元素的减少而减少。

当切片的容量不够时,那么它会扩展至原来的两倍,如果切片中的数据量大于等于 1024 的话,那么每次扩容 1.25 倍。

在声明切片时,可以指定切片的长度及容量,如果不指定容量,那么默认长度就是其容量。

//指定长度及容量
sli1 := make([]int, 5, 10)
fmt.Println(len(sli1))  //5
fmt.Println(cap(sli1))  //10
//不指定容量,那么长度就是其容量
sli2 := make([]int, 5)
fmt.Println(len(sli2)) //5
fmt.Println(cap(sli2)) //5

你可以将切片想象成一个数组上方的窗口,你只能通过这个窗口看见数组的一部分元素,这个窗口的大小就是切片的大小,而数组就是切片底层的数组。

例如上面声明的切片 slie1,长度是 5,容量是 10,那么切片能够看到的数组中的元素便是下标为 0 - 4 的这五个元素。


3.3 集合

Go 语言中的 map 的声明方式有下面的几种:

map1 := map[string]int{}
map1["a"] = 1
map2 := map[string]int{
   "a": 1,
   "b": 2,
}
map3 := make(map[int]string, 10)
map 也可以使用 range 关键字像数组那样进行遍历:
m := map[int]string{
   1: "a",
   2: "b",
   3: "c",
   4: "d",
}
for k, v := range m{
   fmt.Println(k, v)
}

需要注意的是,map 中如果一个键对应的值不存在,那么它的默认值会是零值,例如下面的示例:

m := map[int]int{
   1: 1,
   2: 2,
   3: 3,
   4: 4,
}
fmt.Println(m[5])    //打印出 0

这样的话就会存在一个问题,我们怎么判断一个 map 中键对应的值,到底是不存在还是它的值本来就是 0 呢?其实访问 map 中的值时,它会带有两个返回值,一个返回值是键对应的值,另一个则是是否存在,可以借助这个返回值来判断:

i, ok := m[5]
if ok{
   fmt.Println(i)
}else {
   fmt.Println("不存在键为5")
}

了解了 map,你可能会很自然的想到 set,是的,set 也是一个非常重要并且常用的数据结构,但是在 Go 语言中并没有 set 的实现,只不过我们可以使用 map 来实现一个 set,具体看代码:

set := map[int]bool{}
//存放数据
set[1] = true
set[4] = true
set[4] = true
//删除数据
delete(set, 1)
//判断元素是否存在
if set[1] {
   fmt.Println("存在 1")
}else {
   fmt.Println("不存在 1")
}
//元素的个数
size := len(set)

上面的程序基本完成了常见的 set 的常用功能,其实现也很简单,就是改造了一个 map,其键就相当于我们 set 的值,而 map 键对应的值是一个 bool 值,如果为 true 则表示存在于 set 中,如果为 false 则表示不存在。


3.4 字符串

Go 语言中的字符串 string 是数据类型,而不是引用类型或者指针类型,这也是为什么前面提到的,string 的默认值是空字符串,而不是 nil。

字符串也可以像数组一样进行截取和遍历操作:

s := "I like Golang"
//截取
fmt.Println(s[1])
fmt.Println(s[1:4])
//遍历
for i, v := range s{
   fmt.Printf("%d, %c \n", i, v)
}

字符串的常用操作方法都在 strings 和 strconv 包下面,下面代码给出了几个示例:

s := "1,2,3"
//字符串分割
spliS := strings.Split(s, ",")
fmt.Println(spliS)
//是否包含
c := strings.Contains(s, "1")
fmt.Println(c)
//替换
reS := strings.Replace(s, ",", "-", -1)
fmt.Println(reS)
//strconv 包中的函数,主要是和其他数据类型的转换
v, _ := strconv.ParseBool("false")
fmt.Println(v)

Go 语言的基础语法介绍的第一篇文章就结束了,如果大家有不懂的地方,或者对文中的内容有疑议,欢迎与我交流!

我一直认为编程是一项熟能生巧的手艺,只有多写代码才能够在实践当中提升自己的编程能力。

为此我在 Github 上新建了一个学习项目,使用 Java、Golang、Python 三种语言实现常见的数据结构和算法,以此做为练习编程的素材,你可以多看看代码,并且自己多动手编写,项目地址是 :

https://github.com/roseduan/algo-learn

相关文章
|
1天前
|
Java 编译器 Go
探索Go语言的性能优化技巧
在本文中,我们将深入探讨Go语言的底层机制,以及如何通过代码层面的优化来提升程序性能。我们将讨论内存管理、并发控制以及编译器优化等关键领域,为你提供一系列实用的技巧和最佳实践。
|
1天前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
1天前
|
安全 Go 调度
探索Go语言的并发模式:协程与通道的协同作用
Go语言以其并发能力闻名于世,而协程(goroutine)和通道(channel)是实现并发的两大利器。本文将深入了解Go语言中协程的轻量级特性,探讨如何利用通道进行协程间的安全通信,并通过实际案例演示如何将这两者结合起来,构建高效且可靠的并发系统。
|
1天前
|
安全 Go 开发者
破译Go语言中的并发模式:从入门到精通
在这篇技术性文章中,我们将跳过常规的摘要模式,直接带你进入Go语言的并发世界。你将不会看到枯燥的介绍,而是一段代码的旅程,从Go的并发基础构建块(goroutine和channel)开始,到高级模式的实践应用,我们共同探索如何高效地使用Go来处理并发任务。准备好,让Go带你飞。
|
2天前
|
运维 Go 开发者
Go语言在微服务架构中的应用与优势
本文深入探讨了Go语言在构建微服务架构中的独特优势和实际应用。通过分析Go语言的核心特性,如简洁的语法、高效的并发处理能力以及强大的标准库支持,我们揭示了为何Go成为开发高性能微服务的首选语言。文章还详细介绍了Go语言在微服务架构中的几个关键应用场景,包括服务间通信、容器化部署和自动化运维等,旨在为读者提供实用的技术指导和启发。
|
2天前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
3天前
|
安全 Go 调度
探索Go语言的并发模型:Goroutine与Channel的魔力
本文深入探讨了Go语言的并发模型,不仅解释了Goroutine的概念和特性,还详细讲解了Channel的用法和它们在并发编程中的重要性。通过实际代码示例,揭示了Go语言如何通过轻量级线程和通信机制来实现高效的并发处理。
|
5月前
|
开发框架 安全 中间件
Go语言开发小技巧&易错点100例(十二)
Go语言开发小技巧&易错点100例(十二)
69 1
|
2月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
107 1
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
2月前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID