Словарь и KeyValuePair - PullRequest
       19

Словарь и KeyValuePair

1 голос
/ 01 сентября 2011

У меня проблема со словарем, надеюсь, вы мне поможете.

У меня есть следующая декларация:

class MainCollection<TKey1, TKey2, TValue> : Dictionary<KeyValuePair<TKey1, TKey2>, TValue>

Проблема в том, что я не могу получить элемент из этого словаря по TKey1 ИЛИ TKey2. Есть ли способ получить элемент только с помощью TKey1 ИЛИ TKey2, а не TKey1 И TKey2?

Я написал следующий код:

 public TValue GetItemByKey1(TKey1 key)
 {
     MainCollection<int, int, string> Coll = new MainCollection<int, int, string>();
     var value = from s in Coll where s.Key.Key == key select s.Value;
 }

Но у него уже есть две проблемы:

  1. Ошибка компиляции: s.Key.Key == key => operator == не может применяться к типам int и TKey1
  2. Это выглядит некрасиво. Даже если компиляция будет успешной, я не уверен, что это самый быстрый способ получить такие предметы. Я думаю, что словарь должен что-то лучше.

Как я могу решить такие ошибки? Я не нашел связанных с этим вопросов здесь. Заранее спасибо!

Ответы [ 3 ]

5 голосов
/ 01 сентября 2011

Хорошо, значит, вы хотите иметь возможность поиска по TKey1 или TKey2. Тогда вам нужны три словаря, по одному для каждого из ключей, а затем один для пар ключей.

class Foo<TFirstKey, TSecondKey, TValue> {
    private readonly Dictionary<TFirstKey, List<TValue>> firstDictionary
        = new Dictionary<TFirstKey, List<TValue>>();
    private readonly Dictionary<TSecondKey, List<TValue>> secondDictionary
        = new Dictionary<TSecondKey, List<TValue>>();
    private Dictionary<Tuple<TFirstKey, TSecondKey>, TValue> dictionary
        = new Dictionary<Tuple<TFirstKey, TSecondKey>, TValue>();

    public IEnumerable<TValue> GetByFirstKey(TFirstKey firstKey) {
        return this.firstDictionary[firstKey];
    }

    public IEnumerable<TValue> GetBySecondKey(TSecondKey secondKey) {
        return this.secondDictionary[secondKey];
    }

    public TValue GetByKey(TFirstKey firstKey, TSecondKey secondKey) {
        return this.dictionary[Tuple.Create(firstKey, secondKey)];
    }

    public void Add(TFirstKey firstKey, TSecondKey secondKey, TValue value) {
        this.dictionary.Add(Tuple.Create(firstKey, secondKey), value);
        if(this.firstDictionary.Keys.Contains(firstKey)) {
            this.firstDictionary[firstKey].Add(value);
        }
        else {
            this.firstDictionary.Add(firstKey, new List<TValue> { value });
        }
         if(this.secondDictionary.Keys.Contains(secondKey)) {
            this.secondDictionary[secondKey].Add(value);
        }
        else {
            this.secondDictionary.Add(secondKey, new List<TValue> { value });
        }
    }
}

Обратите внимание, что только поиск по (TFirstKey, TSecondKey) является уникальным, поэтому вам нужно GetByFirstKey и GetBySecondKey для возврата коллекций.

Я оставлю вам остальные детали.

Дело в том, что если вы хотите быстрый поиск по любой клавише, вам нужны два словаря (по одному для каждой координаты пары ключей). Его можно заставить работать с помощью запроса набора ключей, но это медленно (поиск ключей линейный).

1 голос
/ 01 сентября 2011

Я рекомендую не использовать KeyValuePair<TKey, TValue>, потому что KVP является структурой, а наличие ключа в словаре указывает на то, что объект будет некоторое время рядом.Я бы порекомендовал Tuple<T1, T2> вместо этого.Преимущество заключается в том, что Tuple является ссылочным типом, и вы можете свободно передавать его без копирования.Кроме того, Tuple является объектом только для чтения, как и KVPair.Вот как я мог бы написать это:

    class Program
    {
        static void Main(string[] args)
        {
            MainCollection<int, string, DateTime> collection = new MainCollection<int, string, DateTime>();

            collection.Add(Tuple<int, string>.Create(1, "Bob"), new DateTime(1992, 12, 1));
            collection.Add(Tuple<int, string>.Create(2, "James"), new DateTime(1945, 9, 1));
            collection.Add(Tuple<int, string>.Create(3, "Julie"), new DateTime(1976, 7, 15));

            DateTime date;

            date = collection.GetValue(1);
            Console.WriteLine("Bob birthdate: {0}", date);

            date = collection.GetValue("Julie");
            Console.WriteLine("#3 birthdate: {0}", date);

            Console.ReadLine();
        }
    }

    public class MainCollection<TKey1, TKey2, TValue>
    {
        Tuple<TKey1, TKey2> key;
        Dictionary<Tuple<TKey1, TKey2>, TValue> mainCollection = new Dictionary<Tuple<TKey1, TKey2>, TValue>();

        public void Add(Tuple<TKey1, TKey2> Key, TValue Value)
        {
            mainCollection.Add(Key, Value);
        }

        public TValue GetValue(TKey1 Key)
        {
            return mainCollection.Where(k => k.Key.Item1.Equals(Key))
                                 .Select(v => v.Value)
                                 .FirstOrDefault();
        }

        public TValue GetValue(TKey2 Key)
        {
            return mainCollection.Where(k => k.Key.Item2.Equals(Key))
                                 .Select(v => v.Value)
                                 .FirstOrDefault();
        }

    }

    public class Tuple<T1, T2>
    {
        readonly T1 item1;
        readonly T2 item2;

        Tuple(T1 item1, T2 item2)
        {
            this.item1 = item1;
            this.item2 = item2;
        }

        public static Tuple<T1, T2> Create(T1 Item1, T2 Item2)
        {
            return new Tuple<T1, T2>(Item1, Item2);
        }

        public T1 Item1
        { get { return item1; } }

        public T2 Item2
        { get { return item2; } }
    }
}

ПРИМЕЧАНИЕ. Я включил реализацию Tuple, если вы не используете .Net 4.0

Обновление :
Преобразование объекта MainCollection для использования нескольких словарей будет выглядеть так:

public class MainCollection<TKey1, TKey2, TValue>
{
    Tuple<TKey1, TKey2> key;
    Dictionary<TKey1, Tuple<TKey1, TKey2>> k1Dictionary = new Dictionary<TKey1, Tuple<TKey1, TKey2>>();
    Dictionary<TKey2, Tuple<TKey1, TKey2>> k2Dictionary = new Dictionary<TKey2, Tuple<TKey1, TKey2>>();
    Dictionary<Tuple<TKey1, TKey2>, TValue> mainCollection = new Dictionary<Tuple<TKey1, TKey2>, TValue>();

    public void Add(Tuple<TKey1, TKey2> Key, TValue Value)
    {
        mainCollection.Add(Key, Value);

        k1Dictionary.Add(Key.Item1, Key);
        k2Dictionary.Add(Key.Item2, Key);
    }

    public TValue GetValue(TKey1 Key)
    {
        return mainCollection[k1Dictionary[Key]];
    }

    public TValue GetValue(TKey2 Key)
    {
        return mainCollection[k2Dictionary[Key]];
    }
}
1 голос
/ 01 сентября 2011

Просто добавьте метод в саму коллекцию:

 public TValue GetItemByKey1(TKey1 key)
 {         
     var value = from s in this.Keys where s.Key.Key == key select this[s];
     return value.SingleOrDefault();
 }

Вы можете использовать аналогичный метод для TKey2.

Обратите внимание, что эти поиски будут много медленнее, чем стандартный поиск ключа в словаре, поскольку вы выполняете итерацию набора ключей, а не пользуетесь хеш-таблицей, которую иначе использовал бы словарь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...