c#字典中一键很多的值
我想创build一个数据存储来允许我存储一些数据。
第一个想法是创build一个字典,你有一个键值很多,所以有点像一对多的关系。
我觉得字典只有一个关键的价值。
我还可以如何储存这些信息?
从.net3.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 :
查找类似于一个词典。 区别在于Dictionary将键映射到单个值,而Lookup将键映射到值集合。
您可以通过对实现IEnumerable的对象调用ToLookup来创buildLookup的实例。
您可能还想阅读相关问题的 答案 。 欲了解更多信息,请咨询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; } } } }
关于不变性的评论
默认情况下,查找是不可改变的,访问internal
会涉及到reflection。 如果您需要可变性,而不想编写自己的包装器,则可以使用MultiDictionary
MultiValueDictionary
(以前称为MultiDictionary
)(以前不属于Microsoft.Experimental.Collections
一部分)。
您可以使用第二个genericstypes的列表。 例如一个由string键入的string字典:
Dictionary<string, List<string>> myDict;
微软刚刚添加了一个官方的预发布版本正是你正在寻找(称为MultiDictionary)可通过NuGet在这里: https ://www.nuget.org/packages/Microsoft.Experimental.Collections/
关于使用的信息和更多的细节可以通过官方的MSDN博客发现在这里: http : //blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx
我是这个软件包的开发人员,所以如果您对性能或任何问题有任何疑问,请在此处或MSDN中告诉我。
希望有所帮助。
更新
MultiValueDictionary
现在位于corefxlab仓库中 ,您可以从这个 MyGet仓库获取NuGet包。
你的字典的值types可以是一个List或者其他拥有多个对象的类。 就像是
Dictionary<int, List<string>>
对于由整数键控并保存string列表的字典。
select值types的一个主要考虑因素是你将要使用的字典,如果你必须对值进行search或其他操作,那么可以考虑使用一个数据结构来帮助你做你想做的事情 – – 像一个HashSet。
用这个:
Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>>
您可以使用Dictionary<TKey, List<TValue>>
。
这将允许每个键引用一个值列表 。
使用列表(或其他types的集合)的字典,例如:
var myDictionary = new Dictionary<string, IList<int>>(); myDictionary["My key"] = new List<int> {1, 2, 3, 4, 5};
你可以把一个集合(或任何其他types/类)的字典作为一个值。 这样你有一个键,你存储在你的collections价值。
.NET字典只有键和值的1对1关系。 但是,这并不意味着一个值不能是另一个数组/列表/字典。
我想不出一个字典中有一对多关系的理由,但显然有一个。
如果你有不同types的数据要存储到一个密钥,那么这听起来像是创build自己的类的理想时间。 然后你有1到1,但是你有值类存储更多的1个数据。
这是我的方法来实现这种行为。
有关ILookup<TKey, TElement>
的更全面的解决scheme,请查看我的其他答案 。
public abstract class Lookup<TKey, TElement> : KeyedCollection<TKey, ICollection<TElement>> { protected override TKey GetKeyForItem(ICollection<TElement> item) => item .Select(b => GetKeyForItem(b)) .Distinct() .SingleOrDefault(); protected abstract TKey GetKeyForItem(TElement item); public void Add(TElement item) { var key = GetKeyForItem(item); if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)) collection.Add(item); else Add(new List<TElement> { item }); } public void Remove(TElement item) { var key = GetKeyForItem(item); if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)) { collection.Remove(item); if (collection.Count == 0) Remove(key); } } }
用法:
public class Item { public string Key { get; } public string Value { get; set; } public Item(string key, string value = null) { Key = key; Value = value; } } public class Lookup : Lookup<string, Item> { protected override string GetKeyForItem(Item item) => item.Key; } static void Main(string[] args) { var toRem = new Item("1", "different"); var single = new Item("2", "single"); var lookup = new Lookup() { new Item("1", "hello"), new Item("1", "hello2"), new Item(""), new Item("", "helloo"), toRem, single }; lookup.Remove(toRem); lookup.Remove(single); }
注意:密钥必须是不可变的(或者在密钥更改时删除并重新添加)。
你也可以使用;
List<KeyValuePair<string, string>> Mappings;
你可以创build一个非常简单的多字典,它可以自动地插入像这样的值:
public class MultiDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>> { public void Add(TKey key, TValue value) { if (TryGetValue(key, out List<TValue> valueList)) { valueList.Add(value); } else { Add(key, new List<TValue> { value }); } } }
这将创buildAdd
方法的重载版本。 原始的一个允许你插入一个项目的列表,如果还没有这个项目的条目。 这个版本允许你在任何情况下插入一个单一的项目。