GO_05:GO语言基础map与函数
1. map
1. 类似其它语言中的哈希表活着字典,以 key-value 形式存储数据
2. key 必须是支持 == 或 != 比较运算的类型,不可以是函数、map 或 slice
3. map 查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍
4. map使用 make() 创建,支持 := 这种简写方式
5. make([keyType]valueType, cap),cap表示容量,可省略。超出容量时会自动扩容,但尽量提供一个合理的初始值 make([int]string, 10)
6. 使用 len() 获取元素个数
7. 键值对儿不存在时自动添加,使用 delete() 删除某键值对儿,使用 for range 对 map 和 slice 进行迭代操作
package main import (
"fmt"
) func main() {
// map正常初始化
m := make(map[string]string)
m["k1"] = "a"
m["k2"] = "b" fmt.Println("map:", m) v1 := m["k1"] // map中获取数据
fmt.Println("get map k1:", v1) fmt.Println("map len:", len(m)) // 取得map对长度 delete(m, "k2") // 删除map中指定对key值
fmt.Println("deleted map:", m) _, prs := m["k2"] // map通过key取对应对value会返回多值,其中第一个为value值,第二个为状态码(有值返回tru,无值为false)
fmt.Println("prs:", prs) // map初始化的时候进行赋值
n := map[string]int{"foo":, "bar":}
fmt.Println("map:", n)
}
以上代码运行结果如下所示:
map: map[k1:a k2:b]
get map k1: a
map len: 2
deleted map: map[k1:a]
prs: false
map: map[foo:1 bar:2]
2. 使用 range 遍历 slice 和 map
使用for range 对 slice 和 map进行遍历举例
package main import "fmt" // for ... range 类似与其他语言中对foreach
func main() {
// 对切片的循环遍历
s := []int{, , }
fmt.Println("loop slice begin:=================")
// i为切片的下标值,v为对应的值。其中v是对应slice的副本值,对它的修改不会影响原始内容,要想修改原始内容通过指定下标进行覆盖:s[1] = 9
for i, v := range s {
fmt.Println(i, v)
} // 对map对循环遍历
m := map[string]string{"a" : "liang", "b" : "xuli", "c" : "liguang"}
fmt.Println("loop map begin:===================")
for k, v := range m { // k为map对key值,v为对应对value值。其中都为map对拷贝,改变它们对值不会对原始内容造成影响
fmt.Println(k, v)
} // 对slice-map进行循环遍历(终极版)
sm := make([]map[int]string, ) //创建了有5个长度对slice,其中值类型为 map[int]string
fmt.Println("loop slice-map warn begin:========")
for _, v := range sm { // 此时i为slice对下标,v为对应内容即map.这里不需要取key值故使用 _ 表示空白
v = make(map[int]string, ) // 初始化长度为 1 的map
v[] = "OK"
fmt.Println(v)
}
fmt.Println(sm) // 这里打印的结果和循环体中打印的结果有差别,是因为循环体中对应的 v 为slice的拷贝,这点一定要注意 fmt.Println("loop slice-map normal begin:======")
for i := range sm { // 要想改变其中slice内容,则只需要得到下标值通过对下标内容赋值改变原始slice内容,故这里没有去取value
sm[i] = make(map[int]string, )
sm[i][] = "OK"
fmt.Println(sm[i])
}
fmt.Println(sm)
}
以上代码运行结果如下所示:
loop slice begin:=================
0 1
1 3
2 5
loop map begin:===================
a liang
b xuli
c liguang
loop slice-map warn begin:========
map[1:OK]
map[1:OK]
map[1:OK]
map[1:OK]
map[1:OK]
[map[] map[] map[] map[] map[]]
loop slice-map normal begin:======
map[0:OK]
map[0:OK]
map[0:OK]
map[0:OK]
map[0:OK]
[map[0:OK] map[0:OK] map[0:OK] map[0:OK] map[0:OK]]
2.1 对map进行排序
那我们直接上代码,具体的解释和上面一样,在代码中做了详细的介绍,具体代码示例如下:
package main import (
"fmt"
"sort"
) func main() {
// 对map的排序
m := map[int]string{: "a", : "b", : "c", : "d", : "e"}
s := make([]int, len(m))
i :=
for k, _ := range m {
s[i] = k
i++
}
fmt.Println("排序前顺序为:", s)
sort.Ints(s)
fmt.Println("排序后顺序为:", s)
}
以上代码运行结果如下:
排序前顺序为: [3 4 5 1 2]
排序后顺序为: [1 2 3 4 5]
2.2
使用 for range 尝试将类型为 map[int]string 的键和值进行交换,变为类型 map[string]int
package main import "fmt" func main() {
m := map[int]string{: "a", : "b", : "c", : "d", : "e"}
fmt.Println("对调前的map为:", m)
exchangeMap := make(map[string]int, len(m))
for k, v := range m {
exchangeMap[v] = k
}
fmt.Println("对调后的map为:", exchangeMap)
}
以上运行代码结果如下所示:
对调前的map为: map[1:a 2:b 3:c 4:d 5:e]
对调后的map为: map[e:5 a:1 b:2 c:3 d:4]
Slice在应用中可能遇到的坑
我们说过 Slice 是一个可变数组,当容量不够的时候系统会自动扩容一倍之后将原始数据拷贝到新的数组中。那么问题来了,新创建出的类型和之前的类型是不一样的,我们在使用的时候如果还一直使用这个类型取操作里面的数据就会出现问题,具体的问题可以通过以下代码实例看出:
package main import "fmt" func main() {
s := make([]int, )
fmt.Println(s)
BulkAdd(s)
fmt.Println(s)
} func BulkAdd(s []int) {
s = append(s, )
fmt.Println("BulkAdd:", s)
}
运行结果:
[]
BulkAdd: [3]
[]
从以上结果可以看出新增加的内容并未修改了原始的内容,而是作为一个新的类型存在。这种情况下,建议最好使用返回值的方式,这样不论你怎么修改,即使返回的是一个新的类型我在计算的时候也是使用最新的类型来计算的,这样在复杂的程序代码中尤为重要,具体改进如下:
package main import "fmt" func main() {
s := make([]int, )
fmt.Println(s)
s = BulkAdd(s)
fmt.Println(s)
} func BulkAdd(s []int) []int {
s = append(s, )
fmt.Println("BulkAdd:", s)
return s
}
打印结果:
[]
BulkAdd: [3]
[3]
3. 函数 function
1. Go 函数 不支持 嵌套、重载 和 默认参数
2. Go 函数支持一下特性:
<1. 无需声明原型
<2. 不定长度变参
<3. 可以返回多个值
<4. 命名返回值参数
<5. 匿名函数
<6. 闭包
3. 定义函数使用关键字 func,且左大括号不能另起一行
4. 函数也可以作为一种类型使用
先来看一个正常使用func的示例:
package main import "fmt" /**
两个参数都为int类型,返回值类型也为int类型
*/
func plus (a int, b int) int {
return a + b
} /**
3个参数都为int型,那么就可以简写如下形式;返回值可以多个值,并且可以指定返回的类型
*/
func plusPlus (a, b, c int) (int, int) {
return a + b + c, a * b * c
} /**
这里的参数为可变参数列表,其实它就是一个slice
强制规定:可变参数必须放在参互列表的最后位,如果还有其他参数请将其他参数放置其前面
*/
func changeParam (a ...int) int {
fmt.Println(a)
res :=
for _, k := range a {
res += k
}
return res
} func changeParmTwo (value string, a ...int) (string, int) {
fmt.Println(value, a)
res :=
for _, k := range a {
res += k
}
return value, res
} func main() {
res := plus(, )
fmt.Println(res) res_m, res_n := plusPlus(, , )
fmt.Println(res_m, res_n) result := changeParam(, , , , , , )
fmt.Println(result) value, resultTwo := changeParmTwo("liang", , , , , , )
fmt.Println(value, resultTwo) // 匿名函数的使用
a := func() {
fmt.Println("func A() 将其中A省略将函数类型赋给了a,则函数A就变成了匿名函数")
}
a()
}
以上代码运行结果如下所示:
4
7 8
[1 3 4 6 8 9 17]
48
liang [1 5 7 9 21 39]
liang 82
func A() 将其中A省略将函数类型赋给了a,则函数A就变成了匿名函数
那么让我们来解一道数学题,求给定整数的斐波那契数列值
package main import "fmt" //求指定一个数的斐波那契数列
func fact(n int) int {
if n == {
return
}
return n * fact(n - )
} func main() {
fmt.Println(fact())
}
362880
4. 闭包
闭包(closure)是 javascript 的一大难点,也是它的特色。很多高级应用都是依靠闭包来实现的。
4.1 变量作用域
要理解闭包,首先要理解javascript的特殊的变量作用域。变量的作用域无非就两种:全局变量 和 局部变量。
注意语法规则:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
4.2 如何从外部读取函数内部的局部变量?
出于种种原因,我们有时候需要获取到函数内部的局部变量。但是,上面已经说过了,正常情况下,这是办不到的!只有通过变通的方法才能实现。那就是在函数内部,再定义一个函数。
function f1(){
var n=;
function f2(){
alert(n); //
}
}
在上面的代码中,函数 f2 就被包括在函数 f1 内部,这时 f1 内部的所有局部变量,对 f2 都是可见的。但是反过来就不行,f2 内部的局部变量,对 f1 就是不可见的。
子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了嘛。
4.3 闭包的概念
上面代码中的f2函数,就是闭包。各种专业文献的闭包定义都非常抽象,我的理解是: 闭包就是能够读取其他函数内部变量的函数。所以说,闭包可以简单理解成 "定义在一个函数内部的函数"。
所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
4.4 闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。
为什么会这样呢?
原因就在于 f1 是 f2 的父函数,而 f2 被赋给了一个全局变量,这导致 f2 始终在内存中,而 f2 的存在依赖于 f1,因此 f1 也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是 "nAdd=function(){n+=1}" 这一行,首先在 nAdd 前面没有使用 var 关键字,因此 nAdd 是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
4.5 使用闭包注意事项
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
上面说了一大堆关于 javascript 闭包的概念,是因为闭包的概念最先是由 javascript 提出来的,由于这个功能强大故在 Go 中也被引入,那么接下来我们看一下用 Go 怎么实现闭包的效果呢,其实很简单,就是使用我们上面介绍的匿名函数实现:
package main import "fmt" /**
定义了一个闭包函数(closure),传入一个整型参数
声明返回类型为:func(int) int 即返回一个函数
*/
func closure(x int) func(int) int {
return func(y int) int {
return x + y
}
} func main() {
f := closure()
fmt.Println(f())
fmt.Println(f())
}
以上代码运行结果如下:
函数 defer、panic 和 recover 详情请查看我另外一篇博客:http://www.cnblogs.com/liang1101/p/6842230.html
GO_05:GO语言基础map与函数的更多相关文章
- GO语言基础map与函数
1. map 1. 类似其它语言中的哈希表活着字典,以 key-value 形式存储数据 2. key 必须是支持 == 或 != 比较运算的类型,不可以是函数.map 或 slice 3. map ...
- 第二十三节:Java语言基础-详细讲解函数与数组
函数 函数在Java中称为方法,在其他语言中可能称为函数,函数,方法就是定义在类中具有特定功能的程序.函数,在Java中可称为方法. 函数的格式: 修饰符 返回值类型 函数名(参数类型 参数1, 参数 ...
- [11 Go语言基础-可变参数函数]
[11 Go语言基础-可变参数函数] 可变参数函数 什么是可变参数函数 可变参数函数是一种参数个数可变的函数. 语法 如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最 ...
- go语言基础之new函数
1.new函数 表达式new(T)将创建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块内存空间,然后将这块内存空间的地址作为结果返回,而这个结果就是指向这个新的T类型值的指针值,返回的指针 ...
- C语言基础知识【函数】
C 函数1.函数是一组一起执行一个任务的语句.每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数.您可以把代码划分到不同的函数中.如何划分代码到不同的函数 ...
- C语言基础:自定义函数
#include <stdio.h>//声明函数的原型:参数名可以省略 void printRectangle();void printfTriangle();void printhh(l ...
- [java学习笔记]java语言基础概述之函数的定义和使用&函数传值问题
1.函数 1.什么是函数? 定义在类中的具有特定功能的一段独立小程序. 函数也叫做方法 2.函数的格式 修饰符 返回值类型 函数名(参数类型 形式参数1, 参数类型 形式参数2-) { ...
- go语言基础之append函数的使用
1.append函数的使用 作用:在原切片的末尾添加元素 示例: package main //必须有个main包 import "fmt" func main() { s1 := ...
- go语言基础之init函数的介绍
1.init函数的介绍 示例: 文件夹目录如下: 源代码: vi main.go //程序入口 package main //必须 import ( "calc" " ...
随机推荐
- 团队项目开题Scrum Meeting报告
团队项目开题Scrum Meeting报告 在10月30号星期四的晚上我们团队找到了给我们代码的王翊学长,由学长给我们讲解了他编写IOS平台上北航MOOC系统的架构和思路, 因为我们团队没有苹果公司的 ...
- 11.15 Daily Scrum
今天是假期回来的第一个周末,也是我们团队的又一次进度汇总总结和调试工作开展,鉴于一周以来大家的工作有了很大的成果,所以,本次召开的会议主旨在于解决一些开发方面的细节问题,达成共识,为日后进一步的功能方 ...
- OO学习体会与阶段总结(设计与实现)
前言 在最近的一个月的课程中,笔者对于规格化编程进行了深入的学习.运用面向对象抽象思想对编写的程序进行过程抽象.异常处理.数据抽象.类的层次规格与迭代等等规格设计,使得程序结构化程度提高,具有更好 ...
- YQCB冲刺周第五天
站立会议: 任务看板: 今天的任务为依旧为将用户记录的数据添加到数据库中,以及金额球的设置. 遇到的问题为金额球在jsp页面的显示.
- WebService有什么用?
入门之前先简单介绍下WCF.在用WebService做开发时,很多人都不知道WCF和WebService之间的关系.实际上WCF包含了WebService,这是一个很强悍的通信技术应用框架.微软把.N ...
- python learning Regular Expression.py
# 正则表达式,又称规则表达式.(英语:Regular Expression,在代码中常简写为regex.regexp或RE),计算机科学的一个概念.正则表达式通常被用来检索.替换那些符合某个模式(规 ...
- APP案例分析-热血江湖
本次分析的是一款游戏名叫热血江湖,这是我上小学的时候就在玩的游戏,可以说是一直玩到现在所以对它有一定的感情,所以决定分析这款游戏.下面附一张现在的游戏登陆界面. 第一部分 调研, 评测 1.下载软件并 ...
- 怎样利用好单片机上的存储器资源来实现OD的存储与访问
转自:http://www.cnblogs.com/winshton/p/4897789.html 我们知道OD(对象字典)是CANopen的核心,所有功能都是围绕它开展的,是协议栈的数据中心,良好的 ...
- [转帖] CentOS7 与 CentOS6的对比
来源网站: http://blog.51cto.com/fengery/1901349 centos6.x_centos7.x差异改进明细 感谢原作者 centos官方帮助文档:https://wik ...
- mysql-master-ha 实现mysql master的高可用。
常用的mysql 高可用有下面几种方案: 名称 原理 特点 mysqlmha Perl脚本对mysql master做心跳,master down了以后,选举new master ,是要改代理层的 ...