Словарь составного ключа

У меня есть некоторые объекты в Списке, скажем, List<MyClass> и MyClass имеет несколько свойств. Я хотел бы создать индекс списка на основе 3 свойств MyClass. В этом случае 2 из свойств являются интервалом, и одно свойство является датой и временем.

В основном я хотел бы смочь сделать что-то как:

Dictionary< CompositeKey , MyClass > MyClassListIndex = Dictionary< CompositeKey , MyClass >();
//Populate dictionary with items from the List<MyClass> MyClassList
MyClass aMyClass = Dicitonary[(keyTripletHere)];

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

80
задан AaronLS 20 May 2010 в 20:45
поделиться

5 ответов

Вы можете хранить их в структуре и использовать ее в качестве ключа:

struct CompositeKey
{
  public int value1;
  public int value2;
  public DateTime value3;
}

Ссылка для получения хэш-кода: http://msdn.microsoft.com/en-us/library/system.valuetype.gethashcode.aspx

12
ответ дан 24 November 2019 в 09:54
поделиться

На ум сразу приходят два подхода:

  1. Сделайте, как предложил Кевин, и напишите структуру, которая будет служить вашим ключом. Убедитесь, что эта структура реализует IEquatable и переопределите ее методы Equals и GetHashCode.

  2. Напишите класс, который использует вложенные словари внутри. Что-то вроде: TripleKeyDictionary... Этот класс будет иметь внутренний член типа Dictionary>>, и будет раскрывать такие методы, как this[TKey1 k1, TKey2 k2, TKey3 k3], ContainsKeys(TKey1 k1, TKey2 k2, TKey3 k3), и т.д.

* Несколько слов о необходимости переопределения метода Equals: хотя верно, что метод Equals для struct по умолчанию сравнивает значение каждого члена, он делает это с помощью отражения - что неизбежно влечет за собой затраты производительности - и поэтому не является не очень подходящей реализацией для того, что предназначено для использования в качестве ключа в словаре (по моему мнению, во всяком случае). Согласно документации MSDN по ValueType.Equals:

Реализация по умолчанию метода Equals использует отражение для сравнения соответствующих полей obj и данного экземпляра. Переопределите Equals для определенного типа, чтобы улучшить производительность метода и более точно отразить концепцию равенства для данного типа.

4
ответ дан 24 November 2019 в 09:54
поделиться

Как насчет Dictionary>>?

Это позволит вам сделать:

MyClass item = MyData[8][23923][date];
13
ответ дан 24 November 2019 в 09:54
поделиться

Лучший способ, который я мог придумать, - это создать структуру CompositeKey и убедитесь, что переопределяют методы GetHashCode () и Equals (), чтобы обеспечить скорость и точность при работе с коллекцией:

class Program
{
    static void Main(string[] args)
    {
        DateTime firstTimestamp = DateTime.Now;
        DateTime secondTimestamp = firstTimestamp.AddDays(1);

        /* begin composite key dictionary populate */
        Dictionary<CompositeKey, string> compositeKeyDictionary = new Dictionary<CompositeKey, string>();

        CompositeKey compositeKey1 = new CompositeKey();
        compositeKey1.Int1 = 11;
        compositeKey1.Int2 = 304;
        compositeKey1.DateTime = firstTimestamp;

        compositeKeyDictionary[compositeKey1] = "FirstObject";

        CompositeKey compositeKey2 = new CompositeKey();
        compositeKey2.Int1 = 12;
        compositeKey2.Int2 = 9852;
        compositeKey2.DateTime = secondTimestamp;

        compositeKeyDictionary[compositeKey2] = "SecondObject";
        /* end composite key dictionary populate */

        /* begin composite key dictionary lookup */
        CompositeKey compositeKeyLookup1 = new CompositeKey();
        compositeKeyLookup1.Int1 = 11;
        compositeKeyLookup1.Int2 = 304;
        compositeKeyLookup1.DateTime = firstTimestamp;

        Console.Out.WriteLine(compositeKeyDictionary[compositeKeyLookup1]);

        CompositeKey compositeKeyLookup2 = new CompositeKey();
        compositeKeyLookup2.Int1 = 12;
        compositeKeyLookup2.Int2 = 9852;
        compositeKeyLookup2.DateTime = secondTimestamp;

        Console.Out.WriteLine(compositeKeyDictionary[compositeKeyLookup2]);
        /* end composite key dictionary lookup */
    }

    struct CompositeKey
    {
        public int Int1 { get; set; }
        public int Int2 { get; set; }
        public DateTime DateTime { get; set; }

        public override int GetHashCode()
        {
            return Int1.GetHashCode() ^ Int2.GetHashCode() ^ DateTime.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj is CompositeKey)
            {
                CompositeKey compositeKey = (CompositeKey)obj;

                return ((this.Int1 == compositeKey.Int1) &&
                        (this.Int2 == compositeKey.Int2) &&
                        (this.DateTime == compositeKey.DateTime));
            }

            return false;
        }
    }
}

Статья MSDN о GetHashCode ():

http: // msdn .microsoft.com / en-us / library / system.object.gethashcode.aspx

21
ответ дан 24 November 2019 в 09:54
поделиться

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

0
ответ дан 24 November 2019 в 09:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: