Skip to content

Commit

Permalink
更新
Browse files Browse the repository at this point in the history
  • Loading branch information
ffhelicopter committed Feb 23, 2019
1 parent e8beae2 commit 04fcda7
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 64 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Function,Method,Interface,Type等名词是程序员们接触比较多的
联系邮箱:[email protected]


感谢以下网友对本书提出的修改建议: Joyboo 、林远鹏
感谢以下网友对本书提出的修改建议: Joyboo 、林远鹏、Mr_RSI、magic-joker、3lackrush



Expand Down
53 changes: 28 additions & 25 deletions content/42_13_map.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,31 @@
作者:李骁

## 13.1 字典(Map)
**map 是引用类型**,可以使用如下声明:

map是一种元素对的无序集合,一组称为元素value,另一组为唯一键索引key。 未初始化map的值为nil。map 是引用类型,可以使用如下声明:

```Go
var map1 map[keytype]valuetype

var map1 map[string]int
```

[keytype] 和 valuetype 之间允许有空格,但是 Gofmt 移除了空格)

在声明的时候不需要知道 map 的长度,map 是可以动态增长的。

未初始化的 map 的值是 nil。

key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 string、int、float。所以数组、切片和结构体不能作为 key (译者注:含有数组切片的结构体不能作为 key,只包含内建类型的 struct 是可以作为 key 的),但是指针和接口类型可以。如果要用结构体作为 key 可以提供 Key() 和 Hash() 方法,这样可以通过结构体的域计算出唯一的数字或者字符串的 key。
key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 string、int、float。所以数组、函数、字典、切片和结构体不能作为 key (含有数组切片的结构体不能作为 key,只包含内建类型的 struct 是可以作为 key 的),但是指针和接口类型可以。

value 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。map 也可以用函数作为自己的值,这样就可以用来做分支结构:key 用来选择要执行的函数。

map 传递给函数的代价很小:在 32 位机器上占 4 个字节,64 位机器上占 8 个字节,无论实际上存储了多少数据。

**通过 key 在 map 中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢 100 倍;所以如果你很在乎性能的话还是建议用切片来解决问题。**
map 传递给函数的代价很小:在 32 位机器上占 4 个字节,64 位机器上占 8 个字节,无论实际上存储了多少数据。通过 key 在 map 中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢 100 倍;所以如果你很在乎性能的话还是建议用切片来解决问题。

map 可以用 {key1: val1, key2: val2} 的描述方法来初始化,就像数组和结构体一样。

map 是 引用类型 的: 内存用 make 方法来分配。

map 的初始化:
map 是引用类型的,内存用 make 方法来分配。map 的初始化:

```Go
var map1 = make(map[keytype]valuetype)
```

map 容量:
和数组不同,map 可以根据新增的 key-value 对动态的伸缩,因此它不存在固定长度或者最大限制。但是你也可以选择标明 map 的初始容量 capacity,就像这样:make(map[keytype]valuetype,cap)。

Expand All @@ -41,26 +36,30 @@ map 容量:
```Go
map2 := make(map[string]float32, 100)
```

当 map 增长到容量上限的时候,如果再增加新的 key-value 对,map 的大小会自动加 1。所以出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,也最好先标明。

在一个 nil 的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic。

```Go
Works:
可正常运行:

package main
func main() {
var s []int
s = append(s, 1)
}

Fails:
会发生错误:

package main
func main() {
var m map[string]int
m["one"] = 1 // 错误

}
```

map的key访问,val1, isPresent := map1[key1] 或者 val1 = map1[key1] 的方法获取 key1 对应的值 val1。

一般判断是否某个key存在,不使用值判断,而使用下面的方式:
Expand All @@ -71,9 +70,6 @@ if _, ok := x["two"]; !ok {
}

```
**用切片作为 map 的值**

既然一个 key 只能对应一个 value,而 value 又是一个原始类型,那么如果一个 key 要对应多个值怎么办?例如,当我们要处理unix机器上的所有进程,以父进程(pid 为整形)作为 key,所有的子进程(以所有子进程的 pid 组成的切片)作为 value。通过将 value 定义为 []int 类型或者其他类型的切片,就可以优雅的解决这个问题。

这里有一些定义 map 的例子:

Expand All @@ -89,18 +85,18 @@ map3 := map[string]string{}
// map中有三个值
map4 := map[string]string{"a": "1", "b": "2", "c": "3"}
```
从 map1 中删除 key1:
直接 delete(map1, key1) 就可以。如果 key1 不存在,该操作不会产生错误。

从 map1 中删除 key1,直接 delete(map1, key1) 就可以。如果 key1 不存在,该操作不会产生错误。

```Go
Delete(map4, "a")
delete(map4, "a")
```
map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。
如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包)。

## 13.2 "range"语句中更新引用元素的值
map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包)。

在"range"语句中生成的数据的值是真实集合元素的拷贝。它们不是原有元素的引用。
这意味着更新这些值将不会修改原来的数据。同时也意味着使用这些值的地址将不会得到原有数据的指针。
## 13.2 range语句中的值

在"range"语句中生成的数据的值是真实集合元素的拷贝,它们不是原有元素的引用。这意味着更新这些值将不会修改原来的数据。同时也意味着使用这些值的地址将不会得到原有数据的指针。

```Go
package main
Expand All @@ -112,7 +108,11 @@ func main() {
}
fmt.Println("data:", data) // 程序输出: [1 2 3]
}

程序输出:
data: [1 2 3]
```

如果你需要更新原有集合中的数据,使用索引操作符来获得数据。

```Go
Expand All @@ -126,6 +126,9 @@ func main() {

fmt.Println("data:", data) // 程序输出 data: [10 20 30]
}

程序输出:
data: [10 20 30]
```


Expand Down
73 changes: 36 additions & 37 deletions content/42_17_type.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

作者:李骁

## 17.1 Type
Type关键字在Go语言中作用很重要,比如定义结构体,接口,还可以自定义类型,定义类型别名等。自定义类型由一组值以及作用于这些值的方法组成,类型一般有类型名称,往往从现有类型组合通过Type关键字构造出一个新的类型。

## 17.1 Type 自定义类型

在Go 语言中,基础类型有下面几种:

Expand All @@ -11,20 +13,22 @@
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
```
使用 type 关键字可以定义你自己的类型,你可能想要定义一个结构体,但是也可以给一个已经存在的类型的新的名字,然后你就可以在你的代码中使用新的名字(用于简化名称或解决名称冲突),称为自定义类型,如:

使用 type 关键字可以定义我们自己的类型,如我们可以使用type定义一个新的结构体,但也可以把一个已经存在的类型作为基础类型而定义新类型,然后就可以在我们的代码中使用新的类型名字,这称为自定义类型,如:

```Go
type IZ int
```

然后我们可以使用下面的方式声明变量:
这里IZ就是完全是一种新类型,然后我们可以使用下面的方式声明变量:

```Go
var a IZ = 5
```

这里我们可以看到 int 是变量 a 的底层类型,这也使得它们之间存在相互转换的可能。

如果你有多个类型需要定义,可以使用因式分解关键字的方式,例如:
如果我们有多个类型需要定义,可以使用因式分解关键字的方式,例如:

```Go
type (
Expand All @@ -33,25 +37,17 @@ type (
STR string
)
```
在 type TZ int 中,TZ 就是 int 类型的新名称(用于表示程序中的时区),称为自定义类型,然后就可以使用 TZ 来操作 int 类型的数据。使用这种方法定义之后的类型可以拥有更多的特性,且在类型转换时必须显式转换。

每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型),因为 Go 语言是一种静态类型语言
在 type IZ int 中,IZ 就是在 int 类型基础构建的新名称,这称为自定义类型。然后就可以使用 IZ 来操作 int 类型的数据。使用这种方法定义之后的类型可以拥有更多的特性,但是在类型转换时必须显式转换

在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数):
每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型),因为 Go 语言是一种静态类型语言。在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数):

```Go
valueOfTypeB = typeB(valueOfTypeA)
```
类型 B 的值 = 类型 B(类型 A 的值)

type TZ int 中,**新类型不会拥有原类型所附带的方法;TZ 可以自定义一个方法用来输出更加人性化的时区信息。**

```Go
type TZ = int
```
(这种写法应该才是真正的别名,type TZ int 其实是定义了新类型,这两种完全不是一个含义。自定义类型不会拥有原类型附带的方法,而别名是可以的。) 类型别名在1.9中实现,可将别名类型和原类型这两个类型视为完全一致使用。下面举2个例子:

**新类型不会拥有原类型所附带的方法:**
type TZ int 中,**新类型不会拥有原基础类型所附带的方法**,如下面代码所示:

```Go
package main
Expand All @@ -63,26 +59,40 @@ import (
type A struct {
Face int
}
type Aa A
type Aa A // 自定义新类型Aa,没有基础类型A的方法

func (a A) f() {
fmt.Println("hi ", a.Face)
}

func main() {
var s A = A{Face: 9}
var s A = A{ Face: 9 }
s.f()

var sa Aa = Aa{Face: 9}
var sa Aa = Aa{ Face: 9 }
sa.f()
}
```

```Go
编译错误信息:sa.f undefined (type Aa has no field or method f)
```
但如果是类型别名,完整拥有其方法:

通过Type 关键字在原有类型基础上构造出一个新类型,我们需要针对新类型来重新创建新方法。


## 17.2 Type 定义类型别名

```Go
type IZ = int
```

这种写法其实是定义了int类型的别名,类型别名在1.9中实现,可将别名类型和原类型这两个类型视为完全一致使用。type IZ int 其实是定义了新类型,这和类型别名完全不是一个含义。自定义类型不会拥有原类型附带的方法,而别名是拥有原类型附带的。下面举2个例子说明:

如果是类型别名,完整拥有其方法:

```Go

package main

import (
Expand All @@ -92,7 +102,7 @@ import (
type A struct {
Face int
}
type Aa=A
type Aa=A // 类型别名

func (a A) f() {
fmt.Println("hi ", a.Face)
Expand All @@ -105,38 +115,28 @@ func main() {
var sa Aa = Aa{Face: 9}
sa.f()
}
```

```Go

程序输出:
hi 9
hi 9
```

类型可以是基本类型,如:int、float、bool、string;
结构化的(复合的),如:struct、array、slice、map、channel;
只描述类型的行为的,如:interface。

结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。

**函数也可以是一个确定的类型**,就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后,例如
函数也是一个确定的类型,就是以函数签名作为类型。这种类型的定义例如

```Go
func FunctionName (a typea, b typeb) typeFunc
type typeFunc func ( int, int) int
```

你可以在函数体中的某处返回使用类型为 typeFunc 的变量 var
我们可以在函数体中的某处返回使用类型为 typeFunc 的变量 varfunc

```Go
return var
return varfunc
```

一个函数可以拥有多返回值,返回类型之间需要使用逗号分割,并使用小括号 () 将它们括起来,如:

```Go
func FunctionName (a typea, b typeb) (t1 type1, t2 type2)
```
自定义类型不会继承原有类型的方法,但接口方法或组合类型的元素则保留原有的方法。
自定义类型不会继承原有类型的方法,但接口方法或组合类型的内嵌元素则保留原有的方法。

```Go
// Mutex 用两种方法,Lock and Unlock。
Expand All @@ -156,7 +156,6 @@ type PrintableMutex struct {
}
```


>本书《Go语言四十二章经》内容在github上同步地址:https://github.com/ffhelicopter/Go42
>本书《Go语言四十二章经》内容在简书同步地址: https://www.jianshu.com/nb/29056963
>
Expand Down
2 changes: 1 addition & 1 deletion content/42_7_package.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package main表示一个可独立执行的程序,每个 Go 应用程序都包
```Go
import "xx/xx" is a program, not an importable package
```
简单地说,在含有mian包的目录下,你可以写多个文件,每个文件非注释的第一行都使用 package main 来指明这些文件都属于这个应用的 main 包,只有一个文件能有mian() 方法,也就是应用程序的入口。main包不是必须的,只有在可执行的应用程序中需要。
简单地说,在含有main包的目录下,你可以写多个文件,每个文件非注释的第一行都使用 package main 来指明这些文件都属于这个应用的 main 包,只有一个文件能有main() 方法,也就是应用程序的入口。main包不是必须的,只有在可执行的应用程序中需要。

## 7.2 包的导入
一个 Go 程序是通过 import 关键字将一组包链接在一起。
Expand Down

0 comments on commit 04fcda7

Please sign in to comment.