2019独角兽企业重金招聘Python工程师标准>>>

第三章 - 字典 ,数组和切片(Chapter 3 - Maps, Arrays and Slices)

So far we've seen a number of simple types and structures. It's now time to look at arrays, slices and maps.

目前为止我们看了些简单类型和结构体。现在是时候来看看数组,切片和字典了。

数组(Arrays)

If you come from Python, Ruby, Perl, JavaScript or PHP (and more), you're probably used to programming with dynamic arrays. These are arrays that resize themselves as data is added to them. In Go, like many other languages, arrays are fixed. Declaring an array requires that we specify the size, and once the size is specified, it cannot grow:

如果你从Python、Ruby、Perl、JavaScript或者PHP(还有更多),你可能使用过动态数组。这些数组当数据添加进来可以调整大小。在Go中,和其他语言一样,数组是固定大小的。申请一个数组需要我们指定它的大小。申明一个数组时需要指定大小,一旦大小指定了,就不能增长了:

var scores [10]int
scores[0] = 339

The above array can hold up to 10 scores using indexes scores[0] through scores[9]. Attempts to access an out of range index in the array will result in a compiler or runtime error.

上面的这个数组从scores[0]scores[9]可以容纳10个分数。尝试访问超出数组索引的会报编译或者运行时错误。

We can initialize the array with values:

我们可以带值初始化数组:

scores := [4]int{9001, 9333, 212, 33}

We can use len to get the length of the array. range can be used to iterate over it:

我们可能通过len来获取数组的长度。可以用range来迭代数组。

for index, value := range scores {}

Arrays are efficient but rigid. We often don't know the number of elements we'll be dealing with upfront. For this, we turn to slices.

数组高效但不灵活。我们常常不能预先知道有多少元素需要处理。因此,我们使用切片。

切片(Slices)

In Go, you rarely, if ever, use arrays directly. Instead, you use slices. A slice is a lightweight structure that wraps and represents a portion of an array. There are a few ways to create a slice, and we'll go over when to use which later on. The first is a slight variation on how we created an array:

在Go中,你很少,或者根本不,直接使用数组。反而,你使用切片。切片是对数组一个轻量型封装。有几种方式来创建一个切片,我们会全部过一遍。第一种方式和创建数组有一点小小的变化。

scores := []int{1,4,293,4,9}

Unlike the array declaration, our slice isn't declared with a length within the square brackets. To understand how the two are different, let's see another way to create a slice, using make:

和申请数组不同,我们的切片没有在中括号内指定长度。要理解这两者的不同,我们用另一种方式创建切片,使用make

scores := make([]int, 10)

We use make instead of new because there's more to creating a slice than just allocating the memory (which is what new does). Specifically, we have to allocate the memory for the underlying array and also initialize the slice. In the above, we initialize a slice with a length of 10 and a capacity of 10. The length is the size of the slice, the capacity is the size of the underlying array. Using make we can specify the two separately:

我们使用make来替代new因为创建一个切片比只是分配内存(new做的事情)要复杂一些。特别的,我们需要分配底层数组和初始化切片。上面的例子,我们初始化了一个长度和容量为10的切片。长度是切片的大小,容量时底层数组的大小。使用make时可以分开提定这两个值:

scores := make([]int, 0, 10)

This creates a slice with a length of 0 but with a capacity of 10. (If you're paying attention, you'll note that make and len are overloaded. Go is a language that, to the frustration of some, makes use of features which aren't exposed for developers to use.)

这创建了一个长度为0但容量为10的切片。(如果你留心,你会注意到makelen重载的。Go有的时候令人沮丧,一些在使用的功能没有暴露给开发者使用。)

To better understand the interplay between length and capacity, let's look at some examples:

为了更好的理解长度和容量之间的相互作用,让我们来看一个例子:

func main() {scores := make([]int, 0, 10)scores[5] = 9033fmt.Println(scores)
}

Our first example crashes. Why? Because our slice has a length of 0. Yes, the underlying array has 10 elements, but we need to explicitly expand our slice in order to access those elements. One way to expand a slice is via append:

我们的第一个程序崩溃了。为什么呢?因我们的切片的长度为0。是的,底层数组有10个元素,但是我们需要显示的扩展切片来访问这些元素。一种扩展方式是使用append

func main() {scores := make([]int, 0, 10)scores = append(scores, 5)fmt.Println(scores) // prints [5]
}

But that changes the intent of our original code. Appending to a slice of length 0 will set the first element. For whatever reason, our crashing code wanted to set the element at index 5. To do this, we can re-slice our slice:

上面的修改改变了我们的原始代码的意图。扩展一个长度为0的切片会设置第一个元素。无论什么原因,我们崩溃的代码想要的是修改索引为5的元素。 我们可以重切片一次我们的切片:

func main() {scores := make([]int, 0, 10)scores = scores[0:6]scores[5] = 9033fmt.Println(scores)
}

How large can we resize a slice? Up to its capacity which, in this case, is 10. You might be thinking this doesn't actually solve the fixed-length issue of arrays. It turns out that append is pretty special. If the underlying array is full, it will create a new larger array and copy the values over (this is exactly how dynamic arrays work in PHP, Python, Ruby, JavaScript, ...). This is why, in the example above that used append, we had to re-assign the value returned by append to our scores variable: append might have created a new value if the original had no more space.

我们调整切片最大是多少?这是由它的容量决定的,在本例中,是10。你可能会想这没有从本质上解决数能固定和长度的问题。事实上,append是非常特殊的。当底层数组满了,它会创建一个新的数组,并把数值拷贝过来(PHP, Python, Ruby, JavaScript等也是这么做的)。这也就是为什么,我们上面的代码,使用了append之后,我们需要把append的返回值重新赋值给scores的原因:append会产生一个新的值如果原始的空间不足。

If I told you that Go grew arrays with a 2x algorithm, can you guess what the following will output?

如果我告诉你说Go是近两倍的算法来增长数组,那下面的代码会输出什么?

func main() {scores := make([]int, 0, 5)c := cap(scores)fmt.Println(c)for i := 0; i < 25; i++ {scores = append(scores, i)// if our capacity has changed,// Go had to grow our array to accommodate the new dataif cap(scores) != c {c = cap(scores)fmt.Println(c)}}
}

The initial capacity of scores is 5. In order to hold 20 values, it'll have to be expanded 3 times with a capacity of 10, 20 and finally 40.

scores最初的容量是5。为了容纳20个值,它将会扩展3次,分别是10,20和40。

As a final example, consider:

来思考下最后一个例子:

func main() {scores := make([]int, 5)scores = append(scores, 9332)fmt.Println(scores)
}

Here, the output is going to be [0, 0, 0, 0, 0, 9332]. Maybe you thought it would be [9332, 0, 0, 0, 0]? To a human, that might seem logical. To a compiler, you're telling it to append a value to a slice that already holds 5 values.

这里,输出是[0, 0, 0, 0, 0, 9332]。可能你会想它应该是[9332, 0, 0, 0, 0]?对于人来说这可能符合逻辑。对于编译器来说,你告诉它的就是要扩展一个已经有5个值的切片。

Ultimately, there are four common ways to initialize a slice:

最后,有四种常用的方式来初始化一个切片:

names := []string{"leto", "jessica", "paul"}
checks := make([]bool, 10)
var names []string
scores := make([]int, 0, 20)

When do you use which? The first one shouldn't need much of an explanation. You use this when you know the values that you want in the array ahead of time.

何时用哪一种呢?第一种不需要过多的解释。当你知道所有的值并且你要的是数组头的时候使用。

The second one is useful when you'll be writing into specific indexes of a slice. For example:

当你需要写入切片的指定索引时,第二种就很有用。比如:

func extractPowers(saiyans []*Saiyans) []int {powers := make([]int, len(saiyans))for index, saiyan := range saiyans {powers[index] = saiyan.Power}return powers
}

The third version is a nil slice and is used in conjunction with append, when the number of elements is unknown.

当知道有多少元素的时候,就可使用第三种是一个空切片和append配合使用。

The last version lets us specify an initial capacity; useful if we have a general idea of how many elements we'll need.

当我们对需要多少元素有多少了解时使用最后一种方式来指定初始容量。

Even when you know the size, append can be used. It's largely a matter of preference:

即使你知道了大小,append依然可以使用。这很大程度上是一个偏好问题:

func extractPowers(saiyans []*Saiyans) []int {powers := make([]int, 0, len(saiyans))for _, saiyan := range saiyans {powers = append(powers, saiyan.Power)}return powers
}

Slices as wrappers to arrays is a powerful concept. Many languages have the concept of slicing an array. Both JavaScript and Ruby arrays have a slice method. You can also get a slice in Ruby by using [START..END] or in Python via [START:END]. However, in these languages, a slice is actually a new array with the values of the original copied over. If we take Ruby, what's the output of the following?

切片做为数组的封装是一个很有用的概念。许多语言有数组切片的概念。JavaScript和Ruby的数组都有slice方法。你可以对通过[START..END]或者在Python中用[START:END]的方式取得一个切片。但是,在这些语言中切片是对原数组的拷贝。如果我们使用Ruby,下面的代码会输出什么?

scores = [1,2,3,4,5]
slice = scores[2..4]
slice[0] = 999
puts scores

The answer is [1, 2, 3, 4, 5]. That's because slice is a completely new array with copies of values. Now, consider the Go equivalent:

答案是[1, 2, 3, 4, 5]。那是因为slice是一个复制了所有值的全新数组。现在,来看下Go相同的代码:

scores := []int{1,2,3,4,5}
slice := scores[2:4]
slice[0] = 999
fmt.Println(scores)

The output is [1, 2, 999, 4, 5].

输出是[1, 2, 999, 4, 5]

This changes how you code. For example, a number of functions take a position parameter. In JavaScript, if we want to find the first space in a string (yes, slices work on strings too!) after the first five characters, we'd write:

这改变了你的编码方式。比如,一些函数需要一个位置参数。在JavaScript中,如果我们需要找到字符串中第五个字符之后的第一个空格(是的,切片对字符串也是有效的!), 我们这样写:

haystack = "the spice must flow";
console.log(haystack.indexOf(" ", 5));

In Go, we leverage slices:

在Go中,我们使用切片:

strings.Index(haystack[5:], " ")

We can see from the above example, that [X:] is shorthand for from X to the end while [:X] is shorthand for from the start to X. Unlike other languages, Go doesn't support negative values. If we want all of the values of a slice except the last, we do:

上面的例子,我们可以看到,[X:]从X到结束的简写,就如[:X]从开始到X的简写一样。和其他语言不同的是,Go不支持反向取值(这边感觉不对)。如果我们需要切片除了最后一个值以外的所有值,我们这样来写:

scores := []int{1, 2, 3, 4, 5}
scores = scores[:len(scores)-1]

The above is the start of an efficient way to remove a value from an unsorted slice:

以上是一种快速删除未排序的切片中的某个值的方法的开头:

func main() {scores := []int{1, 2, 3, 4, 5}scores = removeAtIndex(scores, 2)fmt.Println(scores)
}func removeAtIndex(source []int, index int) []int {lastIndex := len(source) - 1//swap the last value and the value we want to removesource[index], source[lastIndex] = source[lastIndex], source[index]return source[:lastIndex]
}

Finally, now that we know about slices, we can look at another commonly used built-in function: copy. copy is one of those functions that highlights how slices change the way we code. Normally, a method that copies values from one array to another has 5 parameters: source, sourceStart, count, destination and destinationStart. With slices, we only need two:

最后,既然我们是学习了切片,我们来看另一个常用的内置函数: copycopy是能突出切片是如何改变我们的编码方式的函数之一。通常,数组间拷贝需要5个参数:source, sourceStart, count, destinationdestinationStart。使用切片只要两个:

import ("fmt""math/rand""sort"
)func main() {scores := make([]int, 100)for i := 0; i < 100; i++ {scores[i] = int(rand.Int31n(1000))}sort.Ints(scores)worst := make([]int, 5)copy(worst, scores[:5])fmt.Println(worst)
}

Take some time and play with the above code. Try variations. See what happens if you change copy to something like copy(worst[2:4], scores[:5]), or what if you try to copy more or less than 5 values into worst?

花一些时间来执行上面的代码。多试几次。看看会发生什么,如果将代码改为copy(worst[2:4], scores[:5]),或者要拷贝比5更多或少的值到worst

映射(Maps)

Maps in Go are what other languages call hashtables or dictionaries. They work as you expect: you define a key and value, and can get, set and delete values from it.

Go中的映射在其他语言中叫哈希表或者字典。它们都如你想的一样:你定义一个键和值,然后你可以通过映射来删改查这些值。

Maps, like slices, are created with the make function. Let's look at an example:

映射,和切片一样,是通过make函数来创建的。让我们来看一个例子:

func main() {lookup := make(map[string]int)lookup["goku"] = 9001power, exists := lookup["vegeta"]// prints 0, false// 0 is the default value for an integerfmt.Println(power, exists)
}

To get the number of keys, we use len. To remove a value based on its key, we use delete:

我们使用len来获取有多少键。要通过键来删除一个值,我们用delete

// returns 1
total := len(lookup)// has no return, can be called on a non-existing key
delete(lookup, "goku")

Maps grow dynamically. However, we can supply a second argument to make to set an initial size:

映射是动态增长的。但是,我们可以通过make的第二个参数来设置它的初始大小:

lookup := make(map[string]int, 100)

If you have some idea of how many keys your map will have, defining an initial size can help with performance.

如果知道有多少值,指定初始大小可以有更好的性能表现。

When you need a map as a field of a structure, you define it as:

如果想要把映射做为结构体的一个字段,我们这样定义:

type Saiyan struct {Name stringFriends map[string]*Saiyan
}

One way to initialize the above is via:

初始化的一种方式:

goku := &Saiyan{Name: "Goku",Friends: make(map[string]*Saiyan),
}
goku.Friends["krillin"] = ... //todo load or create Krillin

There's yet another way to declare and initialize values in Go. Like make, this approach is specific to maps and arrays. We can declare as a composite literal:

在Go中有另一种申明和初始化的方式。和make一样,这对映射和数组都是有效的。我们可以像组合文字一样申明:

lookup := map[string]int{"goku": 9001,"gohan": 2044,
}

We can iterate over a map using a for loop combined with the range keyword:

我们可以通过forrange关键字来迭代映射:

for key, value := range lookup {...
}

Iteration over maps isn't ordered. Each iteration over a lookup will return the key value pair in a random order.

映射的迭代器是无序的。每个迭代器随机查找键值对。

指针和值(Pointers versus Values)

We finished Chapter 2 by looking at whether you should assign and pass pointers or values. We'll now have this same conversation with respect to array and map values. Which of these should you use?

通过了解什么时候传值或指针我们结束了第二章。在数组和映射的值方面我们将碰到相同的问题。我们应该用哪一种?

a := make([]Saiyan, 10)
//or
b := make([]*Saiyan, 10)

Many developers think that passing b to, or returning it from, a function is going to be more efficient. However, what's being passed/returned is a copy of the slice, which itself is a reference. So with respect to passing/returning the slice itself, there's no difference.

许多开发人员会想对于一个函数来说是传b还是将它做为返回值更高效。然而,这里传递或者返回的都是一个切片的拷贝,它本身就是一个引用。所以就传递或者返回这个切片而言,没有什么区别。

Where you will see a difference is when you modify the values of a slice or map. At this point, the same logic that we saw in Chapter 2 applies. So the decision on whether to define an array of pointers versus an array of values comes down to how you use the individual values, not how you use the array or map itself.

当你改变一个切片或者映射的值时,你会看见不同。在这点上,同样的逻辑,我们在第二章看到已经适用。所以是否定义一个数组指针还是一个数组值主要归结于如何使用单个值,而不是你如何使用数组或者映射本身。

继续之前(Before You Continue)

Arrays and maps in Go work much like they do in other languages. If you're used to dynamic arrays, there might be a small adjustment, but append should solve most of your discomfort. If we peek beyond the superficial syntax of arrays, we find slices. Slices are powerful and they have a surprisingly large impact on the clarity of your code.

在Go中数组和映射的工作方式与其他语言非常像。如果你用过动态数组,可能有会有一些需要调整,但是通过append可以解决所有的不适。如果我们抛开数组表面的语法,我们就会发现切片。切片是相当强大的,使用切片对你代码的整洁性有着非常巨大的影响。

There are edge cases that we haven't covered, but you're not likely to run into them. And, if you do, hopefully the foundation we've built here will let you understand what's going on.

这里有一些边界例子我们没有涉及到,但是你不太可能遇见这些例子。另外,如果你遇到了,希望我们已经打下的基础能让你理解这是怎么回事。

转载于:https://my.oschina.net/airhead/blog/2980484

Go Little Book - 第三章 - 字典 ,数组和切片相关推荐

  1. 第三章 Matlab数组

    第三章 Matlab数组 一维数组 一维数组的创建 一维数组中元素的提取 二维数组 二维数组的创建 二维数组中元素的提取 字符数组 字符数组的创建 字符数组的拼接 字符数组的显示 空数组 一维数组 一 ...

  2. map的key可以试一个数组吗?_【自考】数据结构第三章,数组,期末不挂科指南,第5篇...

     数组 概念如下 数组可以看成线性表的一种推广,其实就是一种线性表,一维数组又称为向量 数据由一组具有相同类型的数据元素组成,并存储在一组连续的存储单元中 若一维数组中的数据元素又是一维数组结构,则 ...

  3. Day06 第三章:数组

    1 2 3.一维数组 package com.atguigu.java;/** 一.数组的概述* 1.数组的理解:是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据 ...

  4. Javascript第三章创建数组的两种方式第一课

    跟java的区别 ,js中数值没有长度限制,大小可以自动调节, 而java 中的数组是线性的,放同样的类型,而且容量是定制,有限制 跟JAVA的区别 直接在控制台输入对象名,直接能把数组中的数全部查出 ...

  5. php学习第三章:数组处理函数(二)

    六.数据的查找.替换 1.in_array($value,$array);判断$value是否存在于$array这个数组中,如果存在返回true,如果不存在返回false 2.array_search ...

  6. C++基础与深度解析第三章:数组、vector与字符串

  7. 《学习JavaScript数据结构与算法》第三章 数组

    文章目录 前言 一.创建 && 初始化数组 二.操作数组 push-添加元素于末尾 unshift-添加元素于开头 pop-从数组末尾开始删除元素 shift-从数组开头开始删除元素 ...

  8. Java学习 第三章 数组(三)排序算法

    ** Java学习 第三章 数组(三)排序算法 ** 主要内容:排序算法.排序算法横向比较.Arrays工具类的使用.数组常见异常 1.数组中涉及到的常见算法:排序算法 1.1 排序算法分类:内部排序 ...

  9. Java学习 第三章 数组(一)一维数组

    第三章 数组 3-1 数组的概述 数组:多个相同类型数据按一定的顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理. 数组相关概念: 数组名.下标(索引.角标).元素.数组的长 ...

最新文章

  1. 技术“摸鱼” 大神,国外小哥 5 年白拿 45 万工资!
  2. 中断服务子程序(ISR)
  3. Bitcoin 地址原理(2)私钥、公钥、地址基本概念
  4. java的http请求头信息_HTTP请求头所包含的信息
  5. windows 下Android的开发准备
  6. 线性代数:特征值有重根时,相同特征值对应的不同特征向量顺序能交换吗?
  7. kali linux怎么编译软件包,如何解决Kali Linux apt-get安装:E:无法找到软件包checkinstall...
  8. ssh 免密码登录远程主机 免登录运行指令
  9. std::move()源码分析
  10. Valgrind的Memcheck快速入门
  11. hibernate一级缓存_Hibernate缓存–一级缓存
  12. uva - Broken Keyboard (a.k.a. Beiju Text)(链表)
  13. Java 设计模式 之 观察者模式(Observer)
  14. vs2017 安装Qt VS Tools ,新建项目没有Qt GUI Application选项 ,解决方法
  15. dht11温湿度传感器工作原理引脚功能电路接线图
  16. 电脑蓝屏的几种状况处理
  17. java 将网页表格导出_Java导出网页表格Excel过程详解
  18. pnpm install出现:ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies
  19. 【Maven】简介信息
  20. php程序如何删除文件夹和文件

热门文章

  1. BBV:实验基本块向量生成工具
  2. 对人工智能神经网络的认识
  3. Python解析xml文件,此实例将xml设置为模版(from lxml import etree)
  4. 用navicat连接数据库报错:1130-host ... is not allowed to connect to this MySql server如何处理
  5. Qt:Windows编程—Qt实现本地服务管理
  6. 查找算法:折半查找算法实现及分析
  7. 20155117王震宇 2006-2007-2 《Java程序设计》第5周学习总结
  8. for语句 2017-03-17
  9. word20161206
  10. memcache两种客户端比较