[Golang]Map的一个绝妙特性

原文链接:http://studygolang.com/articles/2494


好了,现在回来看看这个文章。

过去几周,我看到的人们对Go语言的热情和语言的发展势头真是让我无比惊叹。这里面的一部分原因可能是和Gophercon 2014有关,在我写这篇文章的时候,刚刚举办完。我对那些能参加的人真是羡慕,嫉妒,恨啊!从会议计划和讨论话题来看,会议确实很棒。另外能够去和Rob Pike神搞个基,以及看看那些家伙用Go创造出来的好东西也是很不错的。另外我感觉最近关于Go的博客数量也爆发了。而且,很多人开始重点地将Go在他们的服务中使用。比如Digital Ocean,大规模云计算创业公司,刚刚宣布他们把一大坨Perl代码改为Go实现,并且极大地改进了诸如响应时间等问题。

我从来不会写那种屌丝级必备的“哇塞,我用Golang两周了,真的好棒啊!”这种文章。因为我觉得这些文章没有啥意思,零价值。但是,最近我遇到了一个Go特性,而且我认为这个特性非常酷,并且在我看来反映出了Go作为一门牛逼语言的基本姿态。

Go的map元素遍历顺序(使用range关键字)是随机的,而不是遵循元素的添加顺序。这是什么意思?很特别么?

Maps

Map的简单介绍。
Andrew Gerrand关于maps的文章直接偷过来用。

计算机科学里面最有用的数据结构之一就是hash表。不同的hash表实现提供了很多独特的特性。但是基本上都包括元素查询,添加和删除。Go提供了一个内置的类型map,这个类型实现了hash表的基本功能。

所以在Go语言里面如果你需要使用hash表,那么就用map吧。因为Go是强类型语言,所以你必须为map的键和对应的值指定具体的类型。这些键或值的类型可以是字符串,整型,指向结构体的指针等。一个常见的用法就是键和值都是字符串类型。
go m := make(map[string]string)

使用方法很简单。在元素被赋值之前,key可以不存在,甚至在被取值的时候也可以不存在(这样就返回零值,零值对于不同的类型是不同的,比如整型是0,而字符串是空字符串"")。

m["bandName"] = "Funny Bones"             // "create"
websiteTitle := m["bandName"] + " Music"  // "read"
m["bandName"] = "Moon Taxi"               // "update"
delete(m, "bandName")                     // "delete"
fmt.Printf(m["bandName"])                 // prints nothing since m["bandName"] == ""

 

可以使用range关键字来遍历map的所有元素。

<pre name="code" class="plain">for key, value := range m {
  fmt.Println("Key:", key, "Value:", value)
}


 
 

遍历顺序
第一眼看上去,Go程序员或许会以为下面的代码输出:

<pre name="code" class="plain">package main

import "fmt"

func main() {
  blogArticleViews := map[string]int{
      "unix": 0,
      "python": 1,
      "go": 2,
      "javascript": 3,
      "testing": 4,
      "philosophy": 5,
      "startups": 6,
      "productivity": 7,
      "hn": 8,
      "reddit": 9,
      "C++": 10,
  }
  for key, views := range blogArticleViews {
      fmt.Println("There are", views, "views for", key)
  }
}


 
 

会是这样的:

<pre name="code" class="plain">$ go run map_iteration_order.go
There are 0 views for unix
There are 1 views for python
There are 2 views for go
There are 3 views for javascript
There are 4 views for testing
There are 5 views for philosophy
There are 6 views for startups
There are 7 views for productivity
There are 8 views for hn
There are 9 views for reddit
There are 10 views for C++


 
 

但从Go 1版本开始,map的遍历顺序是随机的。也就是说下面的结果更有可能:

<pre name="code" class="plain">$ go run map_iteration_order.go
There are 3 views for javascript
There are 5 views for philosophy
There are 10 views for C++
There are 0 views for unix
There are 1 views for python
There are 2 views for go
There are 4 views for testing
There are 6 views for startups
There are 7 views for productivity
There are 8 views for hn
There are 9 views for reddit


 
 

Go语言的设计者们注意到人们过于依赖这种通常情况下key的存储顺序和key的添加顺序一致的特性。所以他们把key的遍历顺序随机化了。因此,如果你希望key的输出顺序和添加顺序一致的话,你需要自己去追踪哪个值存储在哪个位置,就像这样:

<pre name="code" class="plain">package main

import (
    "fmt"
    "sort"
)

func main() {
    var m = map[string]int{
        "unix":         0,
        "python":       1,
        "go":           2,
        "javascript":   3,
        "testing":      4,
        "philosophy":   5,
        "startups":     6,
        "productivity": 7,
        "hn":           8,
        "reddit":       9,
        "C++":          10,
    }
    var keys []string
    for k := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)
    for _, k := range keys {
        fmt.Println("Key:", k, "Value:", m[k])
    }
}


 
 

上面的代码是又一次厚颜无耻地从Andrew 的大作偷过来直接用的。

我觉得大家对这个特性的态度可以分为两类。
第一类人会有各种反应,从不明白为什么要这么做,到有点不爽,甚至是强烈反对。这些人大部分是喜欢弄些有潜在危险性的或者小技巧的代码,并且他们希望Go语言的设计者也能满足他们的愿望。
另一类人倒是能够完全接受,而且很感激Go设计者们能够为他们着想,不断完善和改进Go语言。

为什么这很特别?

一句话:态度。

这个无伤大雅的语言特性在我看来恰是作为通用语言哲学的一个闪光点。没有过于灵活地允许马马虎虎的代码,Go强迫你从一开始就把事情弄得直接一点。Go程序员参考里面说如果他们的程序可以编译(而且代码符合Go的风格),那么代码有很大的可能可以像预期的那样工作,这种模模糊糊却不错的感觉也有Go严谨性的贡献。没有诡异的类型bug,丢失分号等等错误。

尤其是,在Andrew的参考文章中,他指出这是Go的设计者们所作出的改变。他们不再允许人们依赖于那些破破烂烂的假设。我最痛恨的一点就是那些破烂的,到处是bug的功能(这发生在交付的产品中,或者是编程语言,等等很多地方),通过权衡,接受,从而变成了一个特性,另外尝试修复这些"特性"真的是很恶心。很明显的,PHP和JavaScript的语言文化就是因为各种原因往这个方向发展的(他们使用它,但是注定要付出代价,而且很多东西到最后都是没有解决的)。

例如,PHP的一个最大的缺点是,针与干草堆的问题(在干草堆里面找针)。我理想中的语言所应该具有的特点和这种不一致性格格不入。这也是为什么我发现Go的设计者拒绝糟糕的异常和泛型设计。他们就是想做正确的事情,当然他们知道这需要花费时间。他们不着急,而且向语言中添加特性比删除特性容易多了。

总结
Go是一种令人愉悦的语言,而且很多方面都是经过深思熟虑的。不要因为它缺少一些你常用的功能,比如泛型和动态类型,而急于去评断和批评它。如果你自己愿意试一试,你会发现你不一定需要这些功能。而且,你通过使用简单的并行功能会写出更简单,整洁,优雅的代码。

Go一直在坚定地成长和发展中,这也是Go所能带来的乐趣之一。它绝对是可靠的,而且可以用于生产环境中。同时Go的性能和稳定性也在不断地提高。看看下面由Rob Pike最近贴出来的对比Go 1和最新版(快1.3了)的对比。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值