Set 能比 Hashtable 更快吗?
Can Set be faster than Hashtable?
集合通常作为哈希表实现,其中键是集合成员,值是某个常量(True 或其他)。
由于哈希表是集合的泛化,因此可以更快地实现集合,但是有没有更快的实现?也许这样的实现根本不基于哈希表?或者也许是,但以其他方式优化?
容器没有“速度”这样的东西。每个容器都支持一些操作,每个操作都有其复杂性和首选用例。有些容器适合连续读取,有些适合随机访问,有些适合插入,有些适合追加等等。在您开始询问哪种容器实现最适合您的用例之前,您需要描述这个用例。如果我们甚至不知道这个用例是什么,没有人会说哈希表是否最适合您的情况。
如果有一个容器在所有情况下都是最好的,那么我们都会使用它,不会有其他容器。
也就是说,我建议您不要尝试优化标准库容器。很有可能,它们对您来说已经足够了,无需额外干预。如果事实证明您的应用程序太慢并且分析显示容器正是问题所在,则应该在最后进行优化。通常他们不是。
Can Set be faster than Hashtable?
“集合”是跟踪不同键的任何容器,支持操作“插入(键)”、“擦除(键)”和“有(键)”。
散列 table 可用于执行此操作 - 例如C++ 的 std::unordered_set
.
可以使用平衡二叉树来做到这一点——例如C++ 的 std::set
.
虽然对于超过几十或数百个 Key 来说效率不高,但连续内存(即数组或 std::vector
)可以存储排序的元素以进行相当快的(但仍然是 O(log N))二进制搜索查找,插入和擦除要慢得多。有时这可能是最佳选择,因为它 CPU 缓存友好。但是,因为它很快变得低效,C++ 不提供具有这三个集合操作(插入(键)、擦除(键)、查找(键)如上所述)的连续存储容器......你必须下载一个额外的图书馆或自己写。 C++ 确实有 std::binary_search
、std::lower_bound
和 std::upper_bound
这将使它易于实现。
回到你的问题:
Can Set be faster than Hashtable?
它们不一定是不同的东西。一个集合可以使用散列table来实现,有时这可能是最好的选择(特别是当有很多键时,很好地散列键并不昂贵足以避免过多的冲突,并且比较键的成本很低。即便如此,也有不同类型的散列 tables,有时 unordered_set
不是最快的 - 你可以 google “最快的 C++ 哈希 table” 并做一些阅读,例如 https://engineering.fb.com/2019/04/25/developer-tools/f14/
有时其他实现选择——平衡二叉树或连续内存——会比使用散列更快table。
当有一个中等(几千到一百万)元素时,平衡二叉树往往工作良好,并且计算一个键是否“小于”另一个键的成本很低。
转向您的其他问题/断言:
Sets are typically implemented as hashtables where keys are set members and values are some constant (True or something).
错误:sets 仅存储键,它们没有“值”(无论是布尔值还是其他值)。如果您想要键和值,则称为 map。映射可以使用所有与集合相同的数据结构(散列 tables、二叉树、连续存储)来实现,但具有相同的性能折衷。映射很容易实现,类似于 set<std::pair<Key, Value>>
,其中所需的查找操作(散列、等于和/或小于)只考虑键。
我想我已经充分解决了您的其他猜测/问题....
集合通常作为哈希表实现,其中键是集合成员,值是某个常量(True 或其他)。 由于哈希表是集合的泛化,因此可以更快地实现集合,但是有没有更快的实现?也许这样的实现根本不基于哈希表?或者也许是,但以其他方式优化?
容器没有“速度”这样的东西。每个容器都支持一些操作,每个操作都有其复杂性和首选用例。有些容器适合连续读取,有些适合随机访问,有些适合插入,有些适合追加等等。在您开始询问哪种容器实现最适合您的用例之前,您需要描述这个用例。如果我们甚至不知道这个用例是什么,没有人会说哈希表是否最适合您的情况。
如果有一个容器在所有情况下都是最好的,那么我们都会使用它,不会有其他容器。
也就是说,我建议您不要尝试优化标准库容器。很有可能,它们对您来说已经足够了,无需额外干预。如果事实证明您的应用程序太慢并且分析显示容器正是问题所在,则应该在最后进行优化。通常他们不是。
Can Set be faster than Hashtable?
“集合”是跟踪不同键的任何容器,支持操作“插入(键)”、“擦除(键)”和“有(键)”。
散列 table 可用于执行此操作 - 例如C++ 的 std::unordered_set
.
可以使用平衡二叉树来做到这一点——例如C++ 的 std::set
.
虽然对于超过几十或数百个 Key 来说效率不高,但连续内存(即数组或 std::vector
)可以存储排序的元素以进行相当快的(但仍然是 O(log N))二进制搜索查找,插入和擦除要慢得多。有时这可能是最佳选择,因为它 CPU 缓存友好。但是,因为它很快变得低效,C++ 不提供具有这三个集合操作(插入(键)、擦除(键)、查找(键)如上所述)的连续存储容器......你必须下载一个额外的图书馆或自己写。 C++ 确实有 std::binary_search
、std::lower_bound
和 std::upper_bound
这将使它易于实现。
回到你的问题:
Can Set be faster than Hashtable?
它们不一定是不同的东西。一个集合可以使用散列table来实现,有时这可能是最好的选择(特别是当有很多键时,很好地散列键并不昂贵足以避免过多的冲突,并且比较键的成本很低。即便如此,也有不同类型的散列 tables,有时 unordered_set
不是最快的 - 你可以 google “最快的 C++ 哈希 table” 并做一些阅读,例如 https://engineering.fb.com/2019/04/25/developer-tools/f14/
有时其他实现选择——平衡二叉树或连续内存——会比使用散列更快table。
当有一个中等(几千到一百万)元素时,平衡二叉树往往工作良好,并且计算一个键是否“小于”另一个键的成本很低。
转向您的其他问题/断言:
Sets are typically implemented as hashtables where keys are set members and values are some constant (True or something).
错误:sets 仅存储键,它们没有“值”(无论是布尔值还是其他值)。如果您想要键和值,则称为 map。映射可以使用所有与集合相同的数据结构(散列 tables、二叉树、连续存储)来实现,但具有相同的性能折衷。映射很容易实现,类似于 set<std::pair<Key, Value>>
,其中所需的查找操作(散列、等于和/或小于)只考虑键。
我想我已经充分解决了您的其他猜测/问题....