关于编程集的不流行观点 | AI生成和翻译
问题:关于编程中的 set(集合数据结构),有哪些不受欢迎的观点?
回答:
以下是一些关于集合的真正不受欢迎(或至少是反主流)的观点,你很少能在教程或 Reddit 帖子里看到它们被赞扬:
-
对于大多数实际代码来说,集合被高估了
人们在需要“唯一项”或“快速成员测试”时会立即想到set,但实际上,当数据规模为中小(< ~10 万个元素)或你只进行少量查找时,普通列表或数组通常更快,并且内存使用更少。哈希表的常数因子很高。 -
有序集合(如 Java 的
LinkedHashSet或 C++ 的std::unordered_set+ 手动排序)几乎总是一种代码异味
如果你关心插入顺序或排序顺序,你可能实际上想要一个列表/字典或一个适当的排序结构(树图、B-树、跳表)。混合的“有序集合”类型会鼓励人们将不相关的关注点(唯一性 + 顺序)耦合到一个有漏洞的抽象中。 -
Python 的
set默认不可哈希和不可序列化是一个糟糕的设计
你不能把一个集合放在另一个集合中,也不能在不费周折的情况下可靠地序列化它。这使得嵌套集合基本上不可能,除非到处转换成 frozenset,这很笨拙。大多数语言(Java HashSet, JS Set 等)都能很好地嵌套集合。 -
在许多情况下,集合会使代码更难理解
由于顺序是不确定的,并且迭代顺序在不同运行之间可能会改变(CPython 3.6+ 使其稳定,但它在不同实现之间仍然是非确定性的),使用集合进行任何人类将阅读输出的操作(日志、调试、配置)会产生非确定性输出,这会让人抓狂。 -
使用集合进行成员测试通常是过早优化
当 n 很小且代码更清晰时,x in my_list(O(n)) 完全没问题。微基准测试表明,一旦考虑到哈希计算和缓存未命中,在数千个元素以下的成员测试中,列表胜过集合。 -
不可变集合(frozenset,Guava 中的 ImmutableSet 等)使用不足,并且应该成为默认值
大多数情况下,你创建集合后不需要修改它(查找表、配置、图节点等)。将可变版本设为默认值会鼓励不必要的修改和副作用。 -
集合是“小型固定标志/选项集合”的糟糕选择
人们使用{"fast", "verbose", "dry-run"},而位域/枚举标志整数会更快、更小,并使用位运算提供 O(1) 的并集/交集。 -
语言提供的集合字面量是一个陷阱
Python 的{1, 2, 3}和 JavaScript 的new Set([1, 2, 3])看起来很方便,但它们隐藏了即使你只有 2-3 个常量元素,你也要为完整的哈希表分配付费的事实。许多生产代码中充斥着微小的、一次性的集合,这些集合会扼杀启动时间和内存。
这些观点往往会激起喜欢集合的人的强烈反应,但它们来自于多年对实际代码库的剖析和整理。当你的确需要对大型、动态、可哈希项集合进行 O(1) 平均情况成员测试时,集合非常有用——否则它们通常只是时髦的开销。