Почему Lookup неизменен в С#? - программирование
Подтвердить что ты не робот

Почему Lookup неизменен в С#?

В отличие от Dictionary, вы не можете построить Lookup, добавив элементы по одному. Вы случайно не знаете причину?

Lookup аналогичен multimap в С++; почему мы не можем изменить его на С#? Если мы действительно не можем, как мы можем построить структуру данных multimap в С#?

4b9b3361

Ответ 1

Lookup и ILookup были введены как часть LINQ, который обычно использует более функциональный подход, чем другие аспекты структуры. Лично мне нравится то, что Lookup (по крайней мере публично) неизменен - ​​и я с нетерпением жду более неизменяемых коллекций, доступных.

Если вы хотите создать свою собственную структуру данных мультифайла, просто поддерживайте Dictionary<TKey, List<TValue>> или что-то подобное. Возможно, вам захочется посмотреть на мою реализацию Edulinq Lookup для некоторого образца кода.

Ответ 2

Вот реализация, которую я написал

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" });

    }




}

Ответ 3

У меня была такая же проблема и вопрос. Почему 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);            
    }

Ответ 4

Если вы просто хотите инициализировать поиск для последующего использования, вы можете сделать следующее

List<KeyValuePair<TKey, TValue>> list;

//add items to the list here

ILookup<TKey, TValue> = list.ToLookup(item => item.key, item => item.value);

Это явно не поможет, если вам нужны полные действия чтения/записи, но он должен охватывать несколько случаев использования