最近在闲来无事之际开始学习点 Go 语言,Go 语言在近几年很火,有 Docker 和 Kubernetes 这两大杀器作支撑,它已是云计算领域主流的编程语言了,而且有蚕食部分 C、C++、Java 等领域的趋势,怀着一些对新技术的兴趣,我开始学习了 Go 语言,在对其有了简单的了解以后,我渐渐开始喜欢上这门语言了。python
目前我在工做中所使用的主要的编程语言是 Java,Java 虽然说优秀,可是我对其并无太大的好感,主要是它啰嗦复杂,当初学习它也主要是为了找工做。嗯。。。有一丝丝惭愧。git
看到 Go 语言的 logo 我就以为比较新奇,瞬间心情都变得不错了(Go 语言的 logo 是一只地鼠,英文称 Gopher,如今不少 Go 开发者也称本身为 Gopher 了)。github
好了,闲话说了这么多,开始学习吧。算法
在开始以前,你须要上 Go 官网下载 Go 语言所须要的开发工具包,而后安装,安装的步骤很是简单,跟一个普通的应用程序没有差异,这里再也不赘述了。编程
而后再选择一个本身喜欢的 IDE,我本身使用的是 Goland,固然 Vscode,Atom,Vim 也都有相应的支持 Go 语言的插件,选择适合本身的便可。数组
仍是以一个经典的 Hello World 为例子,开始第一个 Go 语言程序:数据结构
package main import "fmt" func main() { fmt.Println("Hello World!") }
这段代码虽然简单,可是也包括了 Go 语言程序的基本结构:关键字 package
表明程序所在的模块(包),import
引入代码依赖,这两个关键字的做用和 Java 中的十分相似。关键字 func
声明一个函数,实现代码逻辑。app
和其余编程语言最主要的差异是,Go 语言的启动入口 main 函数不支持任何返回值和参数,而且它必需要在名为 main 的 package 下,不然 main 只是一个普通的函数。数据结构和算法
Go 语言中的变量有下面几种声明的方式:编程语言
//1. var a int a = 1 //2. var b string = "2" //3. 短变量声明,自动类型推断 c := 10 //4. var d = 123 //5. var ( x int = 1 y string = "1" )
能够看到,Go 语言的变量声明可使用 var 关键字,后面跟变量名称和类型,也能够不设置变量的类型,Go 会自动推断。
比较特殊的一点是 Go 能够在一行代码中对多个变量赋值(和 Python 同样),这样的话常见的交换两个变量的值就能够写得很是简洁了:
m, n := 10, 20 m, n = n, m //交换 m n 的值
Go 语言中的常量主要经过关键字 const
来实现,声明的方式通常有下面两种:
const ABC = "TEST" const( A = 1 B C = "123" )
Go 语言中的数据类型有下面表格中的这些:
Go 数据类型 | |
---|---|
布尔值 | bool |
字符串 | string |
整数 | int int8 int16 int32 int64 |
无符号整数 | uint uint8 uint16 uint32 uint64 uintptr |
字节 | byte //uint8 的别称 |
Unicode 字符 | rune // int32 的别称 |
浮点数 | float32 float64 |
复数 | complex32 complex64 |
数据类型和主流的编程语言其实没有太大的差异,只不过须要注意下面几点:
var a int = 1 var b int64 b = a //报错 b = int64(a) //只能这样转换
type MyInt int func main() { var a int = 1 var b MyInt b = a //报错 }
var s string fmt.Println("*" + s + "*") //初始化 fmt.Println(len(s)) fmt.Println(s == "") //true
Go 中的运算符其实没什么可说的,若是你有其余编程语言的经验,那么能够彻底忽略掉这一部分可有可无的内容,由于它和其余主流的编程语言没有什么差异。
第一类运算符是算术运算符,有下列几种:
这里须要注意一点的是,Go 语言中不支持前置的自增和自减运算符,因此 ++A
和 --A
这种表达式是没法经过编译的。
第二类是比较运算符,这没什么可说的:
在 Go 语言中,若是是比较数组的话,那么参与比较的数组的长度必须保持一致,不然没法经过编译:
x := [...]int{1, 2, 3} y := [...]int{1, 2, 3} //y := [...]int{1, 2, 3, 4} 没法和 x 进行比较 fmt.Println(x == y)
第三类是逻辑运算符:
第四类是位运算符:
Go 语言中的条件语句仍然使用关键字 if 来实现,只不过 if 后面的条件表达式不会加上圆括号:
func oddOrEven(n int) { if n % 2 == 0{ fmt.Printf("%d is even\n", n) }else { fmt.Printf("%d is odd\n", n) } }
这里须要注意的一点是 Go 语言中的 if 条件能够支持变量赋值,以下:
if i := 1 == 1; i{ fmt.Println("i == 1") }else { fmt.Println("i != 1") }
固然这段代码仅仅是用来作演示,无任何实际的意义,if 条件中的赋值语句一般能够用来对一个函数的调用的返回值进行判断,下面是一个简单的示例(Go 语言中的函数支持多返回值,这个后面在介绍函数时会说起):
func main() { if res, err := divide(10, 0); err != nil{ fmt.Println("there is something wrong") }else { fmt.Println(res) } } func divide(x float32, y float32) (float32, error) { if y == 0{ return 0, errors.New("cant`t divide by zero") } return x / y, nil }
上面的代码在 if 条件中调用了函数,函数若是 error 类型的返回值不为空,则说明出现了错误,不然正常输出结果。
还有一个常见的条件控制语句是 switch,Go 语言中也支持,基本的用法和其余的编程语言没有太大的差异:
for i := 0; i < 5; i++ { switch i { case 1, 3: fmt.Println("odd") case 2, 4: fmt.Println("even") default: fmt.Println("not between 0-3") } }
能够看到 case 条件后面能够跟多个条件,而且每一个 case 块中没有带 break 关键字,这是由于默认就有。
Go 语言中的循环只有关键字 for
来支持,而没有常见的 while
,for 关键字能够代替 while 实现其功能,以下:
//常见的for使用 for i := 0; i < 10; i++ { fmt.Println(i * i) } //当作while使用,至关于 while(x > 0) x := 10 for x > 0 { fmt.Println(x * x) x-- } //无限循环,至关于while(true) for { fmt.Println("I love Go") }
先来看一下 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)])
切片能够看作是对数组的一层封装,由于每一个切片的底层必定都是一个数组,先来看看切片的声明方式:
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 的这五个元素。
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 则表示不存在。
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 三种语言实现常见的数据结构和算法,以此作为练习编程的素材,你能够多看看代码,而且本身多动手编写,项目地址是 :