Словарь, где ключ представляет собой пару целых чисел

Мне нужно использовать словарь, где TKey - это пара int.

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

Мне также интересно узнать, будет ли Словарь создавать отдельные записи для двух разных объектов KeyValuePair с тем же ints и почему.

Например:

var myDictionary = new Dictionary<KeyValuePair<int,int>, string>();
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem");
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "SecondItem");
// does the dictionary allow this?

Ответ 1

Возможно, вам стоит рассмотреть возможность использования Tuple

var myDictionary = new Dictionary<Tuple<int,int>, List<string>>(); 
myDictionary.Add(new Tuple<int,int>(3, 3), "FirstItem"); 
myDictionary.Add(new Tuple<int,int>(5, 5), "SecondItem"); 

Согласно документации MSDN, метод Tuple objects Equals будет использовать значения двух объектов Tuple. Это приведет к одной записи на Tuple во внешнем словаре и позволит вам хранить список значений для каждой клавиши.

Ответ 2

Для выполнения словаря требуется ключ, который генерирует уникальный GetHashValue.

KeyValuePair - это тип значения и не рекомендуется для ключа.

ValueType.GetHashCode

Если вы вызываете метод GetHashCode производного типа, возвращаемое значение вряд ли подходит для использования в качестве ключа в хеш-таблице. Кроме того, если значение одного или нескольких из этих полей изменяется, возвращаемое значение может оказаться непригодным для использования в качестве ключа в хеш-таблице. В любом случае, подумайте о том, чтобы написать собственную реализацию Метод GetHashCode, который более точно представляет концепцию хэша кода для типа.

Точка также является типом значения значения, а также не рекомендуется для ключа.
Tuple также генерирует много дубликатов GetHashCode и не является хорошим ключом.

Оптимальный ключ - это тот, который генерирует уникальные ключи.

Рассмотрим UInt16 я и UInt j как два ключа.
Как их можно комбинировать и генерировать уникальный хеш?
Легко объединить их и UInt32.
UInt32 изначально генерирует идеальный хеш.

Алогорифм для упаковки двух UInt16 в UInt32 равен

(i * (UInt16.MaxValue + 1)) + j;

но он еще быстрее с

(UInt32)i << 16 | j;


myDictionary = new Dictionary<UInt32, string>();

С совершенным хешем Словарь - это O (1).
С плохой хэш Словарь становится O (n).

Ответ 3

ОБНОВЛЕНИЕ: исходя из ваших комментариев другим респондентам, приведенный ниже код отвечает на ваши вопросы. Да, дубликаты генерируют исключение, System.ArgumentException

Код, указанный вами, будет работать, но не будет принимать дубликаты KeyValuePairs. System.ArgumentException или подобное будут выбрасываться, если вы добавите KeyValuePair, который уже существует в словаре.

Например, этот код

using System;
using System.Collections;
using System.Collections.Generic;

namespace test{

    public class App {

        public static void Main(string[] args) {
            var myDictionary = new Dictionary<KeyValuePair<int,int>, string>(); 

            Console.WriteLine("Adding 2 items...");
            myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem"); 
            myDictionary.Add(new KeyValuePair<int,int>(5, 5), "SecondItem"); 
            Console.WriteLine("Dictionary items: {0}", myDictionary.Count);

            Console.WriteLine("Adding 2 duplicate items...");
            myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem"); 
            myDictionary.Add(new KeyValuePair<int,int>(5, 5), "SecondItem"); 
            Console.WriteLine("Dictionary items: {0}", myDictionary.Count);
        }
    }
}

дает следующее

Microsoft (R) Visual С# компилятор версии 4.0.30319.17626 для Microsoft (R).NET Framework 4.5 Авторское право (C) Корпорация Microsoft. Все права защищены.

Добавление двух элементов... Словарь: 2 Добавление двух повторяющихся элементов...

Необработанное исключение: System.ArgumentException: элемент с тем же ключ уже добавлен. в System.Collections.Generic.Dictionary`2.Insert(ключ TKey, значение TValue, Boolean add) в test.App.Main(String [] args)

Ответ 4

Словарь требует реализации равенства для определения того, являются ли ключи равными. Вы можете указать реализацию универсального интерфейса IEqualityComparer<T> с помощью конструктора, который принимает параметр сравнения; если вы не указали реализацию, используется стандартный разделитель равенства EqualityComparer<T>.Default.

Итак, в вашем случае, потому что вы не укажете IEqualityComparer<T>, будет использоваться значение по умолчанию.

EqualityComparer<T>.Default проверяет, реализует ли тип T интерфейс System.IEquatable<T>, и, если это так, возвращает EqualityComparer, который использует эту реализацию. В противном случае он возвращает EqualityComparer<T>, который использует переопределения Object.Equals и Object.GetHashCode, предоставленные T.

T struct KeyValuePair не реализует System.IEquatable<T>, поэтому использует метод Equal и GetHashCode struct KeyValuePair. Эти два метода используют как Key, так и Value для проверки равенства и генерации хэш-кода:

public override int GetHashCode()
{
    return Key.GetHashCode() ^ Value.GetHashCode();
}

Итак, подведем итог, в вашем примере словаря не разрешается.

Ответ 5

Просто используйте long клавишу и объедините две клавиши int

public class IntIntDict<T> : Dictionary<long, T>
{
    public void Add(int key1, int key2, T value)
    {
        Add((((long)key1) << 32) + key2, value);
    }

    //TODO: Overload other methods
}

ОБНОВИТЬ

С# 7 представляет новую структуру ValueTuple вместе с упрощенным синтаксисом кортежей. Эти кортежи пригодятся для составных ключей. Вы можете объявить свой словарь и добавить записи следующим образом:

var myDictionary = new Dictionary<(int,int), string>();
myDictionary.Add((3, 3), "FirstItem"); 
myDictionary.Add((5, 5), "SecondItem");

и искать значения, как это

string result = myDictionary[(5, 5)];

или же

if (myDictionary.TryGetValue((5, 7), out string result)) {
    //TODO: use result
}

Ответ 6

Использование KeyValuePair в качестве ключа для вашего словаря:

Он будет функционально работать, чтобы использовать KeyValuePair как ключ в словаре; однако концептуально, вероятно, не лучший выбор для вашего приложения, так как это подразумевает связь Key-Value между двумя ints.

Вместо этого, как утверждает Майк, вы должны использовать Tuple для своего ключа.

Во второй вопрос:

var myDictionary = new Dictionary<KeyValuePair<int,int>, string>();  
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem");  
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "SecondItem");  
// does the dictionary allow this?  

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

var myDictionary = new Dictionary<KeyValuePair<int,int>, List<string>>();  

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

public static class DictionaryHelper
{

    public static void Add(this Dictionary<Tuple<int,int>, List<string>> dict,Tuple<int,int> key, string value)
    {
        if(dict.ContainsKey(key))
        {
            dict[key].Add(value);
        }
        else
        {
            dict.Add(key, new List<string>{value});
        }
    } 
}