我在几年前的Java论坛上也有同样的问题。他们告诉我Set接口是已经定义好的,不能更改,因为这会破坏Set接口当前的实现。然后,他们开始说一些胡言乱语,就像你在这里看到的:“Set不需要get方法”,并开始向我灌输Map必须始终用于从Set中获取元素的观念。
如果您仅将Set用于数学运算,例如交集或并集,则可能只需要contains()方法。但是,在集合中定义Set是用于存储数据的。我使用关系数据模型解释了需要在Set中使用get()方法。
接下来,一个SQL表就像一个类。列定义属性(Java中称为字段),记录表示类的实例。因此,对象是字段的向量。其中一些字段是主键。它们定义对象的唯一性。这就是Java中contains()方法的作用:
class Element {
public int hashCode() {return sumOfKeyFields()}
public boolean equals(Object e) {keyField1.equals(e) && keyField2.equals(e) && ..}
我不了解数据库内部。但是,您在定义表时只需一次指定关键字段。您只需使用@primary注释关键字段。添加记录到表时,无需第二次指定键。您不会将键与数据分开,就像映射一样。
SQL表是集合。它们不是映射。然而,它们提供了get()方法 ,除了维护唯一性和包含性检查之外。
在《计算机程序设计艺术》中,介绍搜索时,D.Knuth 也说了同样的话:
本章大部分内容都致力于研究一个非常简单的搜索问题:如何找到已经存储了给定标识的数据。
你看,数据是带有标识存储的。不是标识指向数据,而是
数据带有标识。他接着说:
例如,在数值应用程序中,我们可能想要找到f(x),给定x和f值的表;在非数值应用程序中,我们可能想要找到给定俄语单词的英语翻译。
看起来他开始讲述映射。但是,
一般来说,我们假设已经存储了一组N个记录,并且问题是定位适当的记录。通常,我们要求N个键不同,以便每个键唯一地标识其记录。所有记录的集合称为“表”或“文件”,其中单词“表”通常用于指示小文件,“文件”通常用于指示大表。一个大文件或一组文件通常被称为“数据库”。搜索算法使用所谓的参数K进行演示,问题是找到具有K作为其键的记录。尽管搜索的目标是找到与K关联的记录中存储的信息,但本章中的算法通常忽略除键本身之外的所有内容。在实践中,一旦我们定位了K,就可以找到相关数据;例如,如果K出现在位置TABLE + i,则相关数据(或指向它的指针)可能位于位置TABLE + i + 1。
即搜索定位记录的关键字段,而不应将关键字映射到数据中。它们都位于同一记录中,作为Java对象的字段。也就是说,搜索算法检查记录的关键字段,就像在集合中一样,而不是在映射中检查某个远程关键字。
我们有N个要排序的项目;我们称它们为“记录”,整个包含N个记录的集合称为“文件”。每个记录Rj都有一个关键字Kj,它控制着排序过程。通常还存在除关键字以外的其他数据,“卫星信息”对排序没有影响,除了必须作为每个记录的一部分进行携带。
我认为在讨论排序时无需在额外的“关键字集”中复制关键字。
……[“计算机程序设计艺术”,第6章,引言]
实体集是特定实体类型的所有实体的集合或组合[http://wiki.answers.com/Q/What_is_entity_and_entity_set_in_dbms]。单个类的对象共享其类属性。同样,DB中的记录也是如此。它们共享列属性。
集合的一个特殊情况是类范围,它是属于该类的所有对象的集合。类范围允许将类视为关系
... ["数据库系统概念"第6版]
基本上,类描述了所有实例共有的属性。关系型DB中的表也是如此。“您将拥有的最简单的映射是单个属性到单个列的属性映射。” 这就是我所说的情况。
我在证明对象和DB记录之间的类比(同构)时非常冗长,因为有些愚蠢的人不接受它(以证明他们的Set必须没有get方法)
你在回放中看到一些人不理解这个,认为使用 Set 和 get 是多余的?这是因为他们滥用了 map,在 set 的位置使用它,导致了冗余。他们调用 put(obj.getKey(), obj) 存储两个键:原始键作为对象的一部分和副本在 map 的键集中。这种重复就是冗余。它还涉及代码的更多膨胀,并浪费运行时消耗的内存。我不知道数据库内部如何,但良好设计和数据库规范化的原则表明这种重复是一个坏主意 -
必须只有一个真相来源。冗余意味着可能发生不一致:关键映射到具有不同键的对象。不一致是冗余的一种表现形式。
Edgar F. Codd 提出了 DB 规范化 只是为了摆脱冗余及其引起的不一致性。老师们对规范化非常明确:
规范化永远不会生成两个具有一对一关系的表。没有理论上的理由将单个实体分别放置在两个表的单个记录中的某些字段和另一个表的单个记录中的其他字段中。
因此,我们有四个理由,说明为什么在set中使用map实现get是不好的:
- 当我们有一组唯一对象时,使用map是不必要的
- map在运行时存储中引入了冗余
- map在DB(在Collections中)中引入了代码膨胀
- 使用map违反了数据存储规范化
即使您不了解记录集概念和数据规范化,也可以像我们、org.eclipse.KeyedHashSet和C++ STL设计者一样,自己发现这种数据结构和算法。
我因指出这些想法而被禁止在Sun论坛上发言。偏见是反对理性的唯一论据,这个世界被偏见主导。他们不想看到概念以及事物如何不同/改进。他们只看到实际世界,无法想象Java Collections的设计可能存在缺陷并且可以改进。向这样的人提醒理性的东西是危险的。他们教给你他们的盲目,并惩罚你如果你不服从。
2013年12月添加:
SICP还说,DB是具有键控记录的集合,而不是映射:
一个典型的数据管理系统花费大量时间来访问或修改记录中的数据,因此需要一种高效的方法来访问记录。这是通过识别每个记录中的一部分作为识别键来完成的。现在我们将数据库表示为一组记录。