与Dictionary
不同,您无法通过逐个添加元素来构造Lookup
。你碰巧知道原因吗?
Lookup
就像C ++中的multimap
一样;为什么我们不能在C#中修改它?如果我们真的不能,我们如何在C#中构建multimap
数据结构?
答案 0 :(得分:19)
Lookup
和ILookup
是作为LINQ的一部分引入的,它通常采用比框架的其他方面更具功能性的方法。我个人喜欢 Lookup
(至少是公开的)不可变的事实 - 我期待more immutable collections being available。
如果您想创建自己的多地图数据结构,只需维护Dictionary<TKey, List<TValue>>
或类似内容。您可能需要查看我的Edulinq implementation of Lookup
以获取一些示例代码。
答案 1 :(得分:1)
这是我写的一个实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class MultiLookup<Key, Value> : ILookup<Key, Value>
{
Dictionary<Key, HashSet<Value>> Contents = new Dictionary<Key, HashSet<Value>>();
public void Add(Key key, Value value)
{
if (!Contains(key))
{
Contents[key]=new HashSet<Value>();
}
Contents[key].Add(value);
}
public void Add(IEnumerable<Tuple<Key, Value>> items)
{
foreach (var item in items)
{
Add(item.Item1, item.Item2);
}
}
public void Remove(Key key, Value value)
{
if (!Contains(key))
{
return;
}
Contents[key].Remove(value);
if (Contents[key].Count==0)
{
Contents.Remove(key);
}
}
public void RemoveKey(Key key)
{
Contents.Remove(key);
}
public IEnumerable<Key> Keys
{
get
{
return Contents.Keys;
}
}
public int Count
{
get
{
return Contents.Count;
}
}
public bool Contains(Key key)
{
return Contents.ContainsKey(key);
}
private class Grouping : IGrouping<Key, Value>
{
public MultiLookup<Key, Value> _source;
public Key _key;
public Key Key
{
get { return _key; }
}
public static HashSet<Value> Empty = new HashSet<Value>();
public IEnumerator<Value> GetEnumerator()
{
if (!_source.Contains(_key))
{
yield break;
}
else
{
foreach (var item in _source[_key])
{
yield return item;
}
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public IEnumerator<IGrouping<Key, Value>> GetEnumerator()
{
return (from p in Contents
select new Grouping() { _key = p.Key, _source = this }).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerable<Value> this[Key key]
{
get { return Contents[key]; }
}
}
和一个测试用例(可能不详尽)
using FluentAssertions;
using System.Linq;
using Xunit;
public class MultiLookupSpec
{
MultiLookup<int, string> Fixture = new MultiLookup<int,string>();
[Fact]
public void NewLookupShouldBeEmpty()
{
Fixture.Count.Should().Be(0);
}
[Fact]
public void AddingANewValueToANonExistentKeyShouldCreateKeyAndAddValue()
{
Fixture.Add(0, "hello");
Fixture.Count.Should().Be(1);
}
[Fact]
public void AddingMultipleValuesToAKeyShouldGenerateMultipleValues()
{
Fixture.Add(0, "hello");
Fixture.Add(0, "cat");
Fixture.Add(0, "dog");
Fixture[0].Should().BeEquivalentTo(new []{"hello", "cat", "dog"});
}
[Fact]
public void RemovingAllElementsOfKeyWillAlsoRemoveKey()
{
Fixture.Add(0, "hello");
Fixture.Add(0, "cat");
Fixture.Add(0, "dog");
Fixture.Remove(0, "dog");
Fixture.Remove(0, "cat");
Fixture.Remove(0, "hello");
Fixture.Contains(0).Should().Be(false);
}
[Fact]
public void EnumerationShouldWork()
{
Fixture.Add(0, "hello");
Fixture.Add(0, "cat");
Fixture.Add(0, "dog");
Fixture.Add(1, "house");
Fixture.Add(2, "pool");
Fixture.Add(2, "office");
Fixture.Select(s => s.Key).Should().Contain(new[] { 0, 1, 2 });
Fixture.SelectMany(s => s).Should().Contain(new[] { "hello", "cat", "dog", "house", "pool", "office" });
}
}
答案 2 :(得分:0)
我有同样的问题和疑问。为什么Lookup是不可变的?我用IDictionary
的一些扩展方法解决了它public static void Add<TKey,TList,TItem>(this IDictionary<TKey,TList> dict,TKey key,TItem item)
where TList : ICollection<TItem>,new()
{
if(!dict.ContainsKey(key))
{
dict.Add(key, new TList());
}
dict[key].Add(item);
}
public static void Remove<TKey, TList, TItem>(this IDictionary<TKey, TList> dict, TKey key)
where TList : IEnumerable<TItem>, new()
{
if (dict.ContainsKey(key))
{
dict.Remove(key);
}
}
public static TList Items<TKey, TList, TItem>(this IDictionary<TKey, TList> dict, TKey key)
where TList : IEnumerable<TItem>, new()
{
if (dict.ContainsKey(key))
{
return dict[key];
}
return default(TList);
}