字典与清单


30

所以我遇到了Dictionary<int, int>今天的工作。这对我来说似乎很奇怪,因为我可能会改用a List<int>。有区别吗?是否会有用例,其中一种结构优于另一种结构?


1
两个(或多个)给定的int之间是否需要某种关系?然后,地图(该语言的词典)才有意义。
钻机2012年

3
词典对我来说很明显。当您需要快速查找内容时,可以使用字典。
ChaosPandion 2012年

2
@ChaosPandion:List<T>.NET框架中的a是一个随机访问数组,其中查找操作通常比.NET框架Dictionary<int,T>
布朗

2
@DocBrown-仅在使用数字索引作为键的相当奇怪的情况下。否则,使用时查找起来会更快Dictionary<TKey, TValue>
ChaosPandion 2012年

2
@chaos这个问题那个奇怪的情况有关。
MarkJ 2012年

Answers:


32

Dictionary<int, int>如果您的索引除了位置之外还具有特殊含义,则可以使用a 。

我想到的直接示例是在数据库中存储id列和int列。例如,如果您有一[person-id]列和一[personal-pin]列,则可以将它们放入中Dictionary<int, int>。这样可以pinDict[person-id]给您提供PIN码,但是索引是有意义的,而不仅仅是在中的位置List<int>

但是实际上,任何时候只要有两个相关的整数列表,这都可能是合适的数据结构。


如果我的人员ID的范围是0,...,999,并且我必须将个人固定值加载到所有1000个人的内存中,那么我通常会选择List<int>,而不是字典。请参阅下面的答案。
布朗

3
是的,但一本字典可疏
JK。

@jk:这正是我在回答中试图阐述的内容。
布朗

7
个人密码?听起来有点多余。
2012年

嗯,当索引具有“特殊含义”时,在现实情况中,它们很可能没有形成连续范围[0,...,n](尽管这不是强制性的),所以此答案是不是很明显的错误,而是不精确的。但是,恕我直言,该决定不应基于此“特殊含义的事物”,而应仅基于“这些键是否构建大约一个间隔[0,...,n]”。基于投票的数量,我想大多数读者都没有提到这一点。
布朗

28

可以将List视为数组,并将Dictionary视为哈希表。仅在Dictionary需要将有意义的键映射(或关联)到值时使用,而List仅将位置(或索引)映射(或关联)到值。

例如,假设您想存储一个人的年龄与其身高之间的关联。您可以使用Dictionary<int, int>将人的年龄(an int)映射到他们的身高(an int):

Dictionary<int, int> personHeightMap = new Dictionary<int, int>();

personHeightMap.Add(21, 185);
personHeightMap.Add(31, 174);

int height = personHeightMap.ContainsKey(21) ? personHeightMap[21] : -1;

这不是一个非常有用的示例,但要点是,您将无法使用a优雅地完成此操作,List因为它将需要在位置上存储这些值。


7
+1表示List订单打交道,而Dictionary关联打交道。如果您每次都需要按一定顺序获取数据,或者它们之间的相对顺序很重要,那么List就可以采用a 。Dictionaries往往是无序的,并处理映射键->值关系。
KChaloux

2
最后一点同样重要,当您知道要查找的内容时,哈希表的时间约为O(1),而最佳情况下的数组为O(logN)(排序且无重复),而O(N)最坏的情况。
JensG 2014年

1
+1。没有其他人似乎已经解决了该名单在语义有序和类型的字典在语义上查找,这是绝对的点根本,在我看来。
本杰明·霍奇森

15

从语义上讲,Dictionary<int, T>List<T>是非常相似的,两者都是.NET框架的随机访问容器。要将列表替换为字典,您需要在类型中输入一个特殊值T(例如null),以表示列表中的空位。如果T不是类似的可为空的类型int,则可以int?改用它,或者如果只是希望存储正值,则还可以使用特殊值(如-1)来表示空插槽。

您将选择哪一个取决于键值的范围。如果您的键在Dictionary<int, T>整数区间内,并且它们之间没有太多间隔(例如[0,... 100]中有80个值),则a List<T>会更合适,因为按索引访问会更快,并且在这种情况下,与字典相比,内存和时间开销更少。

如果您的键值是int[0,...,1000000]等范围内的100个值,则一个List<T>内存需要容纳1000000个T值,而您的字典将只需要大约100个T值的数量级的内存, 100个int值(加上一些开销,实际上期望大约是用于存储这100个键和值的内存的2倍)。因此,在后一种情况下,字典将更合适。


6
这是imho的重要区别,Dictionary <int,int>可以稀疏
jk。

在那种情况下,我们不能使用List <KeyValuePair <int,int >>吗?哪种方法更适合线性遍历?
Deepak Mishra '18

@DeepakMishra:这里的主要区别是,使用List<KeyValuePair<int,T>>,没有可用的O(1)查找操作。其次,中的元素List<KeyValuePair<int,T>>可以具有独立于其键值的特定顺序。如果您需要后者而非前者,List<KeyValuePair<int,T>>或者List<Tuple<int,T>>可能是更好的选择。如果您同时需要两者,则也有OrderedDictionary
布朗

@DocBrown哪种方法更适合线性遍历(即foreach)和插入操作,而无需直接查找?
Deepak Mishra '18

@DeepakMishra:在软件开发中没有像“通常更好”这样的东西。这里的更好意味着更快,更好地阅读,更少的代码键入,更容易扩展以适应未来的需求。但是总的来说,不要再想太多了,而是采取一种可以正确解决您眼中最简单的问题的方法,检查它是否足够快地达到您的目的,并在发现缺点时仅多加思考。
布朗

6

谁能认为它们等效?

字典稀疏并且允许随机插入,但使有序遍历成为一个问题,List不是稀疏且无序插入很昂贵,它固有地提供有序遍历。

在极少数情况下,一个并没有明显优于另一个。


2

另外:其他编程语言将这种类型的数据结构称为Map,而不是Dictionary。

如果可以将您的数据有意义地定义为键/值对,那么如果您需要使用其键查找值,则Dictionary将提供更快的访问速度。

例如,假设您有一个客户列表。每个客户都包含详细信息,例如姓名和地址以及唯一的客户编号。假设您还有要处理的订单的列表。每个订单将包含正在制作的详细信息,并且需要包括订购者的客户编号。

准备好发货时,您需要找到要发货的地址。如果将客户存储为纯列表,则需要搜索整个列表以找到具有正确客户编号的客户。相反,您可以将客户存储在字典中,并以客户编号为键。现在,该词典将使您无需任何搜索即可一步一步拉出正确的客户。


1

字典使用散列来搜索数据。字典首先为键计算一个哈希值,然后该哈希值导致目标数据桶。之后,需要检查存储桶中的每个元素是否相等。但是实际上,该列表将比在第一项搜索中的词典更快,因为在第一步中没有要搜索的内容。但是在第二步中,列表必须先浏览第一个项目,然后再浏览第二个项目。因此,查找的每一步都花费越来越多的时间。列表越大,花费的时间越长。

更多.... 字典Vs的名单有例子。


-1

如果所讨论的代码正在存储两组相关值,则Dictionary类提供了一种通过键查找值的索引方式。如果只有一组值,但是该组值需要随机访问(也许检查一组键是否存在),并且这些值是唯一的,则HashSet可能是最好的设置类。


-3

这些很好的答案似乎涵盖了基础。

我将提供的另一个考虑是,从编码的角度来看,词典(C#中)更为复杂。在相同的代码库中同时具有列表和字典,这会使您的代码难以维护,因为这两种方法在执行基本操作(例如搜索和编组对象数据)方面都有细微的差异。我的观点是,除非出于正当理由需要词典,否则请使用列表。


8
我不同意。词典/地图是每个软件工程师都应该熟悉的基本数据结构。无论哪种方式:您都需要有正当理由使用任何数据结构;包括清单。
史蒂文·埃弗斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.