命名以可预测顺序存储键的字典结构?

Posted

技术标签:

【中文标题】命名以可预测顺序存储键的字典结构?【英文标题】:Naming a dictionary structure that stores keys in a predictable order? 【发布时间】:2010-11-04 13:39:40 【问题描述】:

注意:虽然我的特定上下文是 Objective-C,但我的问题实际上超越了编程语言的选择。另外,我将其标记为“主观”,因为否则肯定有人会抱怨,但我个人认为这几乎完全是客观的。另外,我知道this related SO question,但由于这是一个更大的问题,我认为最好将其作为一个单独的问题。请不要在没有完全阅读和理解的情况下批评这个问题。谢谢!

我们大多数人都熟悉存储键值关联的dictionary abstract data type,无论我们将其称为映射、字典、关联数组、哈希等,这取决于我们选择的语言。字典的简单定义可以概括为三个属性:

    通过键访问值(而不是通过索引,如数组)。 每个键都与一个值相关联。 每个键都必须是唯一的。

任何其他属性都可以说是为了特定目的的便利或专业化。例如,某些语言(尤其是 php 和 Python 等脚本语言)模糊了字典和数组之间的界限,并且确实为字典提供了排序。尽管这很有用,但这样的添加并不是字典的基本特征。在纯粹意义上,字典的实际实现细节是无关紧要的。

对于我的问题,最重要的观察是 未定义枚举键的顺序 - 字典可以以它认为最方便的任何顺序提供键,这取决于客户根据需要组织它们。

我有created custom dictionaries 强制执行特定的键顺序,包括自然排序顺序(基于对象比较)和插入顺序。很明显,将前者命名为 SortedDictionary 上的一些变体(我实际上已经实现了),但后者的问题更大。我见过LinkedHashMap 和LinkedMap (Java)、OrderedDictionary (.NET)、OrderedDictionary (Flash)、OrderedDict (Python) 和OrderedDictionary (Objective-C)。其中一些更成熟,一些更经过概念验证。

LinkedHashMap 是根据 Java 集合传统中的实现来命名的——“linked”是因为它使用双向链表来跟踪插入顺序,而“hash”是因为它是 HashMap 的子类。除了用户不需要担心这一点之外,类名甚至没有真正表明它的作用。使用 ordered 似乎是现有代码之间的共识,但是关于这个主题的网络搜索也揭示了“ordered”和“sorted”之间的可以理解的混淆,我也有同样的感觉。 .NET 实现甚至对明显的误称有评论,并建议它应该改为“IndexedDictionary”,因为您可以在排序的特定点检索和插入对象。

我正在设计一个框架和 API,我想尽可能智能地命名该类。从我的角度来看,indexed 可能会起作用(取决于人们如何解释它,并基于字典的广告功能),ordered 是不精确的并且有太多的潜力混淆,并且 linked “马上就出来了”(向 Monty Python 道歉)。 ;-)

作为用户,什么名字对您来说最有意义?是否有一个特定的名称可以准确地说明该类的功能? (如果合适的话,我不反对使用稍长一些的名称,例如 InsertionOrderDictionary。)

编辑:另一个很大的可能性(在我下面的回答中讨论)是IndexedDictionary。我不太喜欢“插入顺序”,因为如果您允许用户在特定索引处插入键、重新排序键等,这没有任何意义。

【问题讨论】:

+1 提出了一个很好的问题。可悲的是没有公认的意思。人们有自己的选择。在 .NET 中,“排序”可枚举的接口称为IOrderedEnumerable。事实上,它在 .NET 中甚至不一致。尊重字典的“插入顺序”称为OrderedDictionary。 MS 的内部人士 David M. Kean 说这是用词不当,IndexedDictionary 更合适。最好选择在 Objective-C 世界中被更广泛接受的东西。 【参考方案1】:

我投票给InsertionOrderDictionary。你成功了。

【讨论】:

即使 InsertionOrderDictionary 是这个概念的完美描述性名称......库类的九个音节让我畏缩。【参考方案2】:

正如您在上一段中所说,我认为 InsertionOrder(ed)Dict(ionary) 非常明确;除了按插入顺序返回键外,我看不出如何解释它。

【讨论】:

【参考方案3】:

allKeys 按特定顺序返回键的唯一区别是?如果是这样,我只需将allKeysSortedallKeysOrderdByInsertion 方法添加到标准NSDictionary API。

这个插入顺序字典的目标是什么?与数组相比,它给程序员带来了哪些好处?

【讨论】:

不,这不是唯一的区别。我想连续跟踪键的顺序,而不仅仅是在用户要求所有键时生成一个数组。向 NSDictionary 添加方法的真正问题(通过一个类别,我假设)是你不能添加 ivars,所以这是不可能的。不要忘记 -allKeys 会调用 -keyEnumerator(一个 NSDictionary 原语),这通常是枚举键的更有效方式。 对程序员的好处是他们可以像往常一样通过键将内容存储在字典中,但也可以回忆添加内容的顺序。这对于以特定顺序处理字典内容可能很有用,其中队列是不必要的或不是您需要的。另一个用途可以改编自 Java 的 LinkedHashMap——你可以指定当键被访问或重新插入时,它们应该移动到列表的末尾。 (这是低效的,因为它需要线性搜索,但它可以实现诸如从缓存中逐出最近最少使用的项目之类的事情。)【参考方案4】:

自从发布这个问题后,我开始倾向于像 IndexedDictionaryIndexableDictionary 这样的东西。虽然能够保持任意键排序很有用,但仅将其限制为插入排序似乎是不必要的限制。另外,我的班级已经支持indexOfKey:keyAtIndex:,它们(有目的地)类似于NSArray 的indexOfObject:objectAtIndex:。我强烈考虑添加与 NSMutableArray 的insertObject:atIndex: 匹配的insertObject:forKey:atIndex:

每个人都知道在数组中间插入是低效的,但这并不意味着我们不应该在极少数情况下允许它真正有用。 (此外,如果需要,实现可以秘密使用双向链表或任何其他合适的结构来跟踪排序......)

最大的问题:“索引”或“可索引”是否像“有序”一样模糊或可能令人困惑?人们会想到数据库索引,还是书籍索引等?如果他们假设它是用数组实现的,那会不会有害,或者这可能会简化用户对功能的理解?


编辑:考虑到我正在考虑在将来添加与NSIndexSet 一起使用的方法,因此这个名称更有意义。 (NSArray 有-objectsAtIndexes: 以及为给定索引处的对象添加/删除观察者的方法。)

【讨论】:

我认为“索引”比“可索引”更可取。它更类似于在 Cocoa 中命名事物的方式(例如 NSAttributedString 而不是 NSAttributableString)。我确实喜欢 IndexedDictionary。 我认为“索引”与数据库和高效搜索的关联过于紧密。如果我听到“哦,它是一个索引字典”,我会想“索引?按什么?值?真正快速的键查找?等等” 这很有趣,因为我有点在相反的阵营中,我最强烈地将“索引”与数组联系起来。具有讽刺意味的是,在这种情况下,“索引”实际上与非常快速的键查找相关联。 ;-) 在任何一种情况下,都已适当注明,并感谢您的反馈! 在 Cocoa 的上下文中,“索引”总是在“有序集合中的位置”的意义上使用。 这个名字的一个不幸的缺点是背靠背的“d”声音混合在一起,所以(例如)“NSIndexedDictionary”最终听起来像“NSIndexDictionary”,并且与“ NSIndexSet”,这是一个完全不相关的东西。虽然“OrderedDictionary”听起来像“OrderDictionary”,但它不会导致与现有 Cocoa 类相同的混淆。另外,“有序”比“索引”更容易说。诅咒我们懒惰的舌头和重复的硬辅音!! ;-)【参考方案5】:

乍一看,我同意第一个回复——InsertionOrderDictionary,尽管乍一看“InsertionOrder”的含义有点模棱两可。

在我看来,您所描述的内容几乎与 C++ STL 映射完全一样。据我了解,地图是具有附加规则的字典,包括排序。 STL 简单地称它为“地图”,我认为这很贴切。使用 map 的诀窍是,如果不让它变得多余,你就不能真正给继承一个点头——即“MapDictionary”。这太多余了。 “地图”有点太基本了,留下了很大的误解空间。

虽然“CHMap”在查看您的文档链接后可能不是一个糟糕的选择。

也许是“CHMappedDictionary”? =)

祝你好运。

编辑:感谢您的澄清,您每天都会学到新东西。 =)

【讨论】:

实际上,地图和字典是一回事,正如我在帖子的第一部分中提到的那样。 (不幸的是,C++ 添加了排序并且仍然称它为“map”,可能是为了简洁,但在像你这样的情况下会引起混淆。)因此,“map”和“dictionary”的任何组合都是自动冗余的,因此不是一个好名字.不过,我不能说我同意“插入顺序”是模糊的。人们似乎“明白了”,尽管我认为该术语对于结构所支持的内容来说过于具体。 :-/【参考方案6】:

通过将索引顺序与插入顺序分离,这不是简单地归结为将数组和字典保存在单个对象中吗?我猜我对这类对象的投票是 IndexedKeyDictionary

在 C# 中:

public class IndexedKeyDictionary<TKey, TValue>  

  List<TKey> _keys;
  Dictionary<TKey, TValue> _dictionary;
  ...

  public GetValueAtIndex(int index) 
    return _dictionary[_keys[index]];
  

  public Insert(TKey key, TValue val, int index) 
    _dictionary.Add(key, val);

    // do some array massaging (splice, etc.) to fit the new key
    _keys[index] = key;
  

  public SwapKeyIndexes(TKey k1, TKey k2) 
    // swap the indexes of k1 and k2, assuming they exist in _keys
  

真正酷的是索引值......所以我们有一种方法可以对值进行排序并获得新的键顺序。就像这些值是图形坐标一样,当我们沿着坐标平面向上/向下移动时,我们可以读取键(bin 名称)。你会怎么称呼这种数据结构?索引值字典?

【讨论】:

嗯,它可以归结为简单的对象组合,是的 - 细节不太相关(尽管这不能满足我的需要)。要按照您的建议进行操作,NSDictionary(在 Cocoa 中)已经有一个名为 keysSortedByValueUsingSelector: 的方法,我可以免费继承它,其中“选择器”是在每个值上调用以比较它们的方法的名称。这不会在字典本身中以给定顺序维护值,但它允许根据需要对它们进行排序,这对于几乎所有情况可能完全足够。【参考方案7】:

KeyedArray 呢?

【讨论】:

嗯,问题在于它本质上是一个字典,而不是一个数组。 (事实上​​,组织键的结构甚至不需要是一个数组——链表也可以很好地工作。)不过,我很欣赏输入! :-) 为此而投票。你也有道理。 @QuinnTaylor,虽然我同意“数组”令人困惑,但克里斯的观点是从语义的角度而不是实现的角度。在.NET 中有KeyedCollection,它清楚地传达了它是什么的想法。这听起来像是一个尊重集合的顺序,但也支持键索引。【参考方案8】:

强烈投票给 OrderedDictionary。

“有序”一词的含义正是您要宣传的内容:在遍历项目列表时,选择这些项目有一个定义的顺序。 “索引”是一个实现词——它更多地谈论如何实现排序。索引、链表、树……用户无所谓;数据结构的这一方面应该被隐藏。 “有序”是您提供的附加功能的确切词,无论您如何完成它。

此外,似乎订购的选择可以由用户选择。为什么你不能在你的数据类型上创建允许用户从字母顺序切换到插入时间顺序的方法?在默认情况下,用户会选择一个特定的排序并坚持使用它,在这种情况下,实现的效率不会低于为每个排序方法创建专门的子类。在一些不太常用的情况下,开发人员实际上可能希望根据应用程序上下文对相同数据使用多种不同排序中的任何一种。 (我可以想到我从事过的特定项目,我希望有这样的数据结构可用。)

称它为 OrderedDictionary,因为这正是它的本质。 (坦率地说,我对“字典”这个词的使用有更多的问题,因为这个词在很大程度上暗示了排序,而流行的此类实现不提供它,但这是我最讨厌的。你真的应该能够说“字典”并知道排序是按字母顺序排列的——因为字典就是这样——但这个论点对于流行语言的现有实现来说太迟了。)并允许用户按照他选择的顺序进行访问。

【讨论】:

我明白你的意思,但不同意 index 是一个“实现词”——我同意其他人的观点,并且会说“array”也是如此。索引意味着随机访问,我的字典支持;也就是说,用户可以“直接”访问密钥 0 到 n-1。对于我的口味,我仍然认为“有序”过于接近“排序”——这意味着结构(而不​​是用户)对元素进行了排序。交换排序是可能的,但我已经有一个 SortedDictionary,它在每个元素上调用 -compare: 来确定顺序。它还增加了不必要的复杂性。任何具有多个排序的东西都应该使用组合。 关于“字典”与“地图”之类的术语,您也有一个非常有效的观点。啊,历史的错误。即便如此,如果您考虑术语-定义关系,它仍然是有道理的。 (可以说字典几乎意味着多映射,因为可能有多个定义。)不过,我发现“字典”比“关联数组”(PHP)或简单的“哈希”(感谢 Ruby)更可取。另外,请注意,Objective-C 和 Cocoa 确实使用了该语言特有的一些约定。此外,Objective-C 支持通过类别动态添加方法,因此用户可以添加自己的特定排序。 当我想到(非 compsci)索引时,我想我经常会想到像杜威十进制系统这样的东西:一个分类系统,最重要的是提供快速访问,而不是定义一本书是否是“大于”或“先于”另一本书。当您在键之间定义“先于”关系时。但我也明白你的意思——当然同意“索引”比“关联数组”(gah)之类的要好得多。【参考方案9】:

我投票给 OrderedDictionary,原因如下:

"Indexed" 从未在 Cocoa 类中使用,除非在一个实例中使用。它总是以名词形式出现(NSIndexSet、NSIndexPath、objectAtIndex: 等)。只有一个实例是“Index”作为动词出现,它位于 NSPropertyDescription 的“indexed”属性上:isIndexed 和 setIndexed。 NSPropertyDescription 大致类似于数据库中的表列,其中“索引”是指优化以加快搜索时间。因此,如果 NSPropertyDescription 作为核心数据框架的一部分,“isIndexed”和“setIndexed”就相当于 SQL 数据库中的索引。因此,将其称为“IndexedDictionary”似乎是多余的,因为创建数据库中的索引是为了加快查找时间,但字典已经具有 O(1) 查找时间。然而,称它为“IndexDictionary”也是用词不当,因为 Cocoa 中的“索引”指的是位置,而不是顺序。两者在语义上是不同的。

我理解您对“OrderedDictionary”的担忧,但在 Cocoa 中已经开创了先例。当用户想要维护特定的序列时,他们使用“有序”:-[NSApplication orderedDocuments]、-[NSWindow orderedIndex]、-[NSApplication orderedWindows] 等。所以,John Pirie 的想法基本正确。

但是,您不希望将插入字典成为用户的负担。他们会想要创建一个字典once,然后让它保持适当的顺序。他们甚至不想按特定顺序请求对象。订单规范应在初始化期间完成。

因此,我建议将 OrderedDictionary 设为类集群,其中包含 InsertionOrderDictionary 和 NaturalOrderDictionary 和 CustomOrderDictionary 的私有子类。然后,用户只需像这样创建一个 OrderedDictionary:

OrderedDictionary * dict = [[OrderedDictionary alloc] initWithOrder:kInsertionOrder];
//or kNaturalOrder, etc

对于 CustomOrderDictionary,您可以让他们给您一个比较选择器,或者甚至(如果他们运行的是 10.6)一个块。我认为这将为未来的扩展提供最大的灵活性,同时仍保持适当的名称。

【讨论】:

我之前已经和 Dave 争论过,CoreData 可以说是不是严格的 Cocoa 框架。我喜欢 Dave 的逻辑,但我真的很犹豫为了订购而引入我自己的类集群。我想以尽可能低的复杂性提供灵活的功能。我觉得在这种情况下,私有类集群隐藏了太多细节——类集群的重点是隐藏实现的变体,而不是行为。您无法查看 OrderedDictonary 并知道枚举键的顺序。对我来说,这是一个明显的设计缺陷。 您对 Cocoa 中“有序”的先例提出了很好的观点,尽管我仍然不同意从“但是”开始的所有内容。 :-) 通过指定 NSSortDescriptor、自定义选择器等,任何自动自定义排序都将属于 SortedDictionary 的未来扩展。无需为每个所需的排序顺序创建特殊风格的 SortedDictionary。虽然我在某种程度上仍然喜欢“索引”,但与现有实现(在其他语言中)和 Cocoa 约定的一致胜过个人观点。谢谢!

以上是关于命名以可预测顺序存储键的字典结构?的主要内容,如果未能解决你的问题,请参考以下文章

d3 以可预测的顺序强制布局节点

Innodb存储表结构

根据首选顺序更改字典键的顺序

服务器与本地机器上dict键的随机顺序[重复]

hbase字典顺序存储

Swift - 字典中的存储值顺序完全改变