字典和集合是进行过性能高度优化的 数据结构,特别是对于查找、添加和删除操做。本节将结合实例介绍它们在具体场景下的性能表现,以及与列表等其余数据结构的对比。
例如,有一个存储产品信息(产品 ID、名称和价格)的列表,如今的需求是,借助某件产品的ID找出其价格。则实现代码以下:html
def find_product_price(products, product_id): for id, price in products: if id == product_id: return price return None products = [ (111, 100), (222, 30), (333, 150) ] print('The price of product 222 is {}'.format(find_product_price(products, 222)))
运行结果为:python
The price of product 222 is 30
在上面程序的基础上,若是列表有 n 个元素,由于查找的过程须要遍历列表,那么最坏状况下的时间复杂度就为 O(n)
。即便先对列表进行排序,再使用二分查找算法,也须要 O(logn)
的时间复杂度,更况且列表的排序还须要 O(nlogn)
的时间。
但若是用字典来存储这些数据,那么查找就会很是便捷高效,只需 O(1)
的时间复杂度就能够完成,由于能够直接经过键的哈希值,找到其对应的值,而不须要对字典作遍历操做,实现代码以下:算法
products = { 111: 100, 222: 30, 333: 150 } print('The price of product 222 is {}'.format(products[222]))
运行结果为:服务器
The price of product 222 is 30
有些读者可能对时间复杂度并无直观的认识,不要紧,再给你们列举一个实例。下面的代码中,初始化了含有 100,000 个元素的产品,并分别计算出了使用列表和集合来统计产品价格数量的运行时间:数据结构
# 统计时间须要用到 time 模块中的函数,了解便可 import time def find_unique_price_using_list(products): unique_price_list = [] for _, price in products: # A if price not in unique_price_list: #B unique_price_list.append(price) return len(unique_price_list) id = [x for x in range(0, 100000)] price = [x for x in range(200000, 300000)] products = list(zip(id, price)) # 计算列表版本的时间 start_using_list = time.perf_counter() find_unique_price_using_list(products) end_using_list = time.perf_counter() print("time elapse using list: {}".format(end_using_list - start_using_list)) # 使用集合完成一样的工做 def find_unique_price_using_set(products): unique_price_set = set() for _, price in products: unique_price_set.add(price) return len(unique_price_set) # 计算集合版本的时间 start_using_set = time.perf_counter() find_unique_price_using_set(products) end_using_set = time.perf_counter() print("time elapse using set: {}".format(end_using_set - start_using_set))
运行结果为:app
time elapse using list: 68.78650900000001 time elapse using set: 0.010747099999989018能够看到,仅仅十万的数据量,二者的速度差别就如此之大。而每每企业的后台数据都有上亿乃至十亿数量级,所以若是使用了不合适的数据结构,很容易形成服务器的崩溃,不但影响用户体验,而且会给公司带来巨大的财产损失。
| 哈希值 (hash) 键 (key) 值 (value) . | ... 0 | hash0 key0 value0 . | ... 1 | hash1 key1 value1 . | ... 2 | hash2 key2 value2 . | ...这种结构的弊端是,随着哈希表的扩张,它会变得愈来愈稀疏。好比,有这样一个字典:
{'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'}那么它会存储为相似下面的形式:
entries = [ ['--', '--', '--'] [-230273521, 'dob', '1999-01-01'], ['--', '--', '--'], ['--', '--', '--'], [1231236123, 'name', 'mike'], ['--', '--', '--'], [9371539127, 'gender', 'male'] ]显然,这样很是浪费存储空间。为了提升存储空间的利用率,如今的哈希表除了字典自己的结构,会把索引和哈希值、键、值单独分开,也就是采用以下这种结构:
Indices ---------------------------------------------------- None | index | None | None | index | None | index ... ---------------------------------------------------- Entries -------------------- hash0 key0 value0 --------------------- hash1 key1 value1 --------------------- hash2 key2 value2 --------------------- ... ---------------------
indices = [None, 1, None, None, 0, None, 2] entries = [ [1231236123, 'name', 'mike'], [-230273521, 'dob', '1999-01-01'], [9371539127, 'gender', 'male'] ]经过对比能够发现,空间利用率获得很大的提升。
当向字典中插入数据时,Python 会首先根据键(key)计算出对应的哈希值(经过 hash(key) 函数),而向集合中插入数据时,Python会根据该元素自己计算对应的哈希值(经过 hash(valuse) 函数)。
例如:函数
dic = {"name":1} print(hash("name")) setDemo = {1} print(hash(1))
运行结果为:性能
8230115042008314683 1获得哈希值(例如为 hash)以后,再结合字典或集合要存储数据的个数(例如 n),就能够获得该元素应该插入到哈希表中的位置(好比,能够用 hash%n 的方式)。
具体遇到哈希冲突时,各解决方法的具体含义可阅读《哈希表详解》一节作详细了解。优化
这里的找到空位,表示哈希表中没有存储目标元素。spa
O(1)
。