C#字典 - 一个键,多个值
在.NET 3.5+中,使用Dictionary<IKey, List<IValue>>
的代替方法是使用来自LINQ命名空间的Lookup
:
// Lookup Order by payment status (1:m) // would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed); IEnumerable<Order> payedOrders = byPayment[false];
根据MSDN的介绍:
Lookup<TKey, TElement>类似于Dictionary<TKey, TValue>。不同之处在于,Dictionary<TKey, TValue>将键映射到单个值,而Lookup<TKey, TElement>将键映射到值的集合。
可以通过在实现IEnumerable的对象上调用ToLookup来创建Lookup<TKey, TElement>的实例。
你也可以阅读这个答案来了解相关问题的更多信息。有关更多信息,请查阅MSDN。
完整示例:
using System; using System.Collections.Generic; using System.Linq; namespace LinqLookupSpike { class Program { static void Main(String[] args) { // Init var orderList = new List<Order>(); orderList.Add(new Order(1, 1, 2010, true)); // (orderId, customerId, year, isPayed) orderList.Add(new Order(2, 2, 2010, true)); orderList.Add(new Order(3, 1, 2010, true)); orderList.Add(new Order(4, 2, 2011, true)); orderList.Add(new Order(5, 2, 2011, false)); orderList.Add(new Order(6, 1, 2011, true)); orderList.Add(new Order(7, 3, 2012, false)); // Lookup Order by its id (1:1, so usual dictionary is ok) Dictionary<Int32, Order> orders = orderList.ToDictionary(o => o.OrderId, o => o); // Lookup Order by customer (1:n) // would need something like Dictionary<Int32, IEnumerable<Order>> orderIdByCustomer ILookup<Int32, Order> byCustomerId = orderList.ToLookup(o => o.CustomerId); foreach (var customerOrders in byCustomerId) { Console.WriteLine("Customer {0} ordered:", customerOrders.Key); foreach (var order in customerOrders) { Console.WriteLine(" Order {0} is payed: {1}", order.OrderId, order.IsPayed); } } // The same using old fashioned Dictionary Dictionary<Int32, List<Order>> orderIdByCustomer; orderIdByCustomer = byCustomerId.ToDictionary(g => g.Key, g => g.ToList()); foreach (var customerOrders in orderIdByCustomer) { Console.WriteLine("Customer {0} ordered:", customerOrders.Key); foreach (var order in customerOrders.Value) { Console.WriteLine(" Order {0} is payed: {1}", order.OrderId, order.IsPayed); } } // Lookup Order by payment status (1:m) // would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed); IEnumerable<Order> payedOrders = byPayment[false]; foreach (var payedOrder in payedOrders) { Console.WriteLine("Order {0} from Customer {1} is not payed.", payedOrder.OrderId, payedOrder.CustomerId); } } class Order { // Key properties public Int32 OrderId { get; private set; } public Int32 CustomerId { get; private set; } public Int32 Year { get; private set; } public Boolean IsPayed { get; private set; } // Additional properties // private List<OrderItem> _items; public Order(Int32 orderId, Int32 customerId, Int32 year, Boolean isPayed) { OrderId = orderId; CustomerId = customerId; Year = year; IsPayed = isPayed; } } } }
关于不可变性的说明
默认情况下,lookups是不可变的,访问internal
需要使用反射。
如果需要可变性并且不想编写自己的包装器,可以使用MultiValueDictionary
(以前称为MultiDictionary
)来实现(已经不再更新的Microsoft.Experimental.Collections
的一部分)。
假设键和值都是字符串类型,那么为什么不使用Dictionary<string, HashSet<string>>
而不是Dictionary<string, List<string>>
?列表无法保证给定键的值集合中的唯一性,但是集合可以。
这不是我的回答的重点。如果您想要在值中强制执行唯一性,只需在创建查找之前使用.Distinct()
。如果需要多重集合或保持项目排序或索引,列表似乎是合理的选择。
我同意您的方法的正确性和可行性。使用列表的方法中有一个隐含的假设,即每个键的值集合很小,根据这个假设,您的答案似乎是合适的。然而,从可读性的角度来看,如果我使用一些现成的东西来保持集合中的唯一性(集合),那么我认为它更可读,因为接口/API很清晰,甚至HashSet
类给我的代码读者提供了一个非常清楚的信号(他们不必进入我的实现来看我在做什么)。
您是指完整示例中的第一个列表,对吗?这只是示例中的样板代码,用于展示使用ToLookup
和ILookup
与基于Dictionary
的实现之间的区别。
在这段内容中,出现了一个问题:如何在C#中创建一个字典,其中一个键对应多个值。为了解决这个问题,Microsoft添加了一个名为MultiDictionary的官方预发布版本,可以通过NuGet进行下载。使用和更多细节可以在MSDN的官方博客文章中找到。
更新后,MultiValueDictionary现在在corefxlab repo中,并且可以从MyGet feed获取NuGet包。虽然博客已经三年没有更新了,但是我还没有使用MultiValueDictionary,但它实现了IReadOnlyDictionary,这是不可变的。关于它是否安全使用,是否有任何想法吗?
第二个链接已经失效。过时的内容应该被移除,而不是添加"更新"的部分。请修改你的回答,让它看起来像是今天写的。
下面是整理后的文章:
Microsoft最近添加了一个名为MultiDictionary的官方预发布版本,用于解决在C#中创建一个键对应多个值的字典的问题。你可以通过NuGet来获取这个版本。更多的使用信息和细节可以在Microsoft官方的MSDN博客文章中找到。
这个MultiValueDictionary现在已经移动到了corefxlab repo中,并且可以从MyGet feed获取NuGet包。虽然这个博客已经三年没有更新了,但是MultiValueDictionary实现了IReadOnlyDictionary接口,是不可变的。关于它是否安全使用,是否有任何想法吗?
第二个链接已经失效。过时的内容应该被移除,而不是添加"更新"的部分。请修改你的回答,让它看起来像是今天写的。
在C#中,字典(Dictionary)是一种常用的数据结构,用于存储键值对。然而,有时候我们需要在一个字典中存储多个值,而不是只有一个值。本文将讨论在C#中如何实现一个字典中的一个键对应多个值的情况以及解决方法。
问题的原因是字典(Dictionary)在默认情况下只能存储一个键对应一个值。然而,有时候我们需要在一个键对应多个值的情况下使用字典。例如,我们希望存储一个城市对应多个居民的情况,这时候一个键只对应一个值的字典就无法满足需求了。
为了解决这个问题,我们可以使用C#中的泛型类型List来存储多个值。具体而言,我们可以创建一个字典,其中键的类型是string,值的类型是List
以下是一个示例代码,展示了如何创建一个键对应多个值的字典:
Dictionary> myDict;
在上面的代码中,myDict是一个字典,其中的键是string类型,值是List
使用这种方式,我们可以很方便地向字典中添加多个值。例如,我们可以使用Add方法将一个键与多个值关联起来:
myDict.Add("City1", new List{"Resident1", "Resident2", "Resident3"});
上述代码将"City1"这个键与三个居民关联起来。我们还可以使用索引器来访问字典中的值:
Listresidents = myDict["City1"];
通过上述代码,我们可以获取与"City1"这个键关联的所有居民。
总结而言,通过在C#中使用泛型类型List,我们可以实现一个字典中的一个键对应多个值的需求。这种方式非常灵活,并且可以满足各种多值关联的需求。通过以上的示例代码和解决方法,我们可以方便地处理一个键对应多个值的情况。