5.2 应用案例
散列表用途广泛,本节将介绍几个应用案例。
5.2.1 将散列表用于查找
手机都内置了方便的电话簿,其中每个姓名都有对应的电话号码。
假设你要创建一个类似这样的电话簿,将姓名映射到电话号码。该电话簿需要提供如下功能。
-
添加联系人及其电话号码。
-
通过输入联系人来获悉其电话号码。
这非常适合使用散列表来实现!在下述情况下,使用散列表是很不错的选择。
-
创建映射。
-
查找。
创建电话簿非常容易。首先,新建一个散列表。
phone_book = dict()
顺便说一句,Python提供了一种创建散列表的快捷方式——使用一对大括号。
phone_book = {}
与phone_book = dict()等效
下面在这个电话簿中添加一些联系人的电话号码。
phone_book["jenny"] = 8675309
phone_book["emergency"] = 911 这就成了!现在,假设你要查找Jenny的电话号码,为此只需向散列表传入相应的键。
print phone_book["jenny"]
8675309 Jenny的电话号码
如果要求你使用数组来创建电话簿,你将如何做呢?散列表让你能够轻松地模拟映射关系。
散列表被用于大海捞针式的查找。例如,你在访问像adit.io 这样的网站时,计算机必须将adit.io转换为IP地址。
无论你访问哪个网站,其网址都必须转换为IP地址。
5.2.2 防止重复
假设你负责管理一个投票站。显然,每人只能投一票,但如何避免重复投票呢?有人来投票时,你询问他的全名,并将其与已投票者名单进行比对。
如果名字在名单中,就说明这个人投过票了,因此将他拒之门外!否则,就将他的姓名加入到名单中,并让他投票。现在假设有很多人来投过了票,因此名单非常长。
每次有人来投票时,你都得浏览这个长长的名单,以确定他是否投过票。但有一种更好的办法,那就是使用散列表!
为此,首先创建一个散列表,用于记录已投票的人。
voted = {}
有人来投票时,检查他是否在散列表中。
value = voted.get("tom")
如果“tom”在散列表中,函数get将返回它;否则返回None。你可使用这个函数检查来投票的人是否投过票!
代码如下。
voted = {}
def check_voter(name):
if voted.get(name):
print "kick them out!"
else:
voted[name] = True
print "let them vote!"
我们来测试几次。
check_voter("tom")
let them vote!
check_voter("mike")
let them vote!
check_voter("mike")
kick them out!
首先来投票的是Tom,上述代码打印let them vote!。接着Mike来投票,打印的也是letthem vote!。然后,Mike又来投票,于是打印的就是kick them out!。
别忘了,如果你将已投票者的姓名存储在列表中,这个函数的速度终将变得非常慢,因为它必须使用简单查找搜索整个列表。但这里将它们存储在了散列表中,而散列表让你能够迅速知道来投票的人是否投过票。使用散列表来检查是否重复,速度非常快。
5.2.3 将散列表用作缓存
来看最后一个应用案例:缓存。如果你在网站工作,可能听说过进行缓存是一种不错的做法。下面简要地介绍其中的原理。假设你访问网站facebook.com。
(1) 你向Facebook的服务器发出请求。
(2) 服务器做些处理,生成一个网页并将其发送给你。
(3) 你获得一个网页。
假设你有个侄女,总是没完没了地问你有关星球的问题。火星离地球多远?月球呢?木星呢?每次你都得在Google搜索,再告诉她答案。这需要几分钟。现在假设她老问你月球离地球多远,很快你就记住了月球离地球238 900英里。因此不必再去Google搜索,你就可以直接告诉她答案。这就是缓存的工作原理:网站将数据记住,而不再重新计算。
如果你登录了Facebook,你看到的所有内容都是为你定制的。你每次访问facebook.com,其服务器都需考虑你感兴趣的是什么内容。但如果你没有登录,看到的将是登录页面。每个人看到的登录页面都相同。Facebook被反复要求做同样的事情:“当我注销时,请向我显示主页。”有鉴于此,它不让服务器去生成主页,而是将主页存储起来,并在需要时将其直接发送给用户。
-
用户能够更快地看到网页,就像你记住了月球与地球之间的距离时一样。下次你侄女再问你时,你就不用再使用Google搜索,立刻就可以告诉她答案。
-
Facebook需要做的工作更少。
缓存是一种常用的加速方式,所有大型网站都使用缓存,而缓存的数据则存储在散列表中!
Facebook不仅缓存主页,还缓存About页面、Contact页面、Terms and Conditions页面等众多其他的页面。因此,它需要将页面URL映射到页面数据。
cache = {}
def get_page(url):
if cache.get(url):
return cache[url]
else:
data = get_data_from_server(url)
cache[url] = data
return data
仅当URL不在缓存中时,你才让服务器做些处理,并将处理生成的数据存储到缓存中,再返
回它。这样,当下次有人请求该URL时,你就可以直接发送缓存中的数据,而不用再让服务器进
行处理了。
5.2.4 小结
这里总结一下,散列表适合用于:
-
模拟映射关系;
-
防止重复;
-
缓存/记住数据,以免服务器再通过处理来生成它们。