键查找 Searching for a key
选择适当的数据结构是一件大家都认同,却很少有人会考虑的事。从我的经验来看, 这不仅是因为它很难引起人们的好奇心,更重要的原因是它需要不厌其烦的进行实验和基准测试,这可能会增加很多不必要的负担。让我们来举个例子。我在日常工作中遇到的很多软件操作都是查找一个key然后对其进行操作,要么检查它是否存在, 要么取出与其相关联的值。为了简单起见, 我们把操作限于检查键是否存在。
基于同样的目的,我们同时将key的类型限制为int。因为int类型是编程语言中最常见、最容易被理解的数据类型。它也是最有可能在各种应用中被用作’id’的类型。
假设我们有一个使用整型id的应用,我们用它来识别客户的身份,我们还打算为它构建一个缓存。缓存是否被命中,决定了我们是否查找数据库。为该需求进行数据结构选择时,hashmap是几乎所有计算机科学学生的首选,它现在在C++中的实现被称为unordered_map。这是由于大O表示法(Big O notation)告诉我们查找哈希表所需要的时间是恒定的。还有哪些备选项?
1. 向量(vector),它的底层实现是一个数组,亦被称为连续的内存(contiguous memory)。
2. 树,它使我们可以进行分类遍历(sorted traversal),但是它也会导致更多的指针和可能的缺页异常(page fault)。
3. 链表(Linked list)。
实际上我们还可以有更多的选项,在这里我就不一一列举了。现在请大家思考一下,既然大O表示法告诉我们使用hashmap已经足够好了, 为什么我们还要考虑其他的选项呢?这是因为有时候大O表示法对我们的应用而言并不能起到很好的指导作用,这时使用小O表示法也许更恰当。我希望读者从中得到的启发是:测量所有东西(Measure Everything)。当需要在自己的机器上进行性能预测时,我们要养成测量的好习惯,因为它是你获得真正答案的唯一方式。 某些人可能会说,“使用向量!那样的话就可以对缓存线(cache lines)进行优化!”事实上,如果我们不把这些不同的方案放到一起进行测量,我们就不可能知道哪个更好。