Кортежи (или массивы) в качестве словарных ключей в C #

Я пытаюсь сделать таблицу поиска словаря на C #. Мне нужно разрешить 3-кортеж значений для одной строки. Я пытался использовать массивы в качестве ключей, но это не сработало, и я не знаю, что еще делать. На данный момент я рассматриваю возможность создания Словаря Словари Словари, но это, вероятно, не очень красиво смотреть, хотя я бы это сделал в javascript.

    7 Solutions collect form web for “Кортежи (или массивы) в качестве словарных ключей в C #”

    Если вы используете .NET 4.0, используйте Tuple:

    lookup = new Dictionary, string>(); 

    Если нет, вы можете определить Кортеж и использовать его в качестве ключа. Tuple должен переопределять GetHashCode, Equals и IEquatable:

     struct Tuple : IEquatable> { readonly T first; readonly U second; readonly W third; public Tuple(T first, U second, W third) { this.first = first; this.second = second; this.third = third; } public T First { get { return first; } } public U Second { get { return second; } } public W Third { get { return third; } } public override int GetHashCode() { return first.GetHashCode() ^ second.GetHashCode() ^ third.GetHashCode(); } public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) { return false; } return Equals((Tuple)obj); } public bool Equals(Tuple other) { return other.first.Equals(first) && other.second.Equals(second) && other.third.Equals(third); } } 

    Между кортежем и подходами, основанными на вложенных словарях, почти всегда лучше использовать кортеж.

    С точки зрения ремонтопригодности ,

    • его гораздо проще реализовать функциональность, которая выглядит так:

       var myDict = new Dictionary, string>(); 

      чем

       var myDict = new Dictionary>>(); 

      со стороны вызываемого лица. Во втором случае каждое дополнение, поиск, удаление и т. Д. Требуют действия более чем на одном словаре.

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

    С точки зрения эффективности , лучший вывод, который вы можете достичь, – это измерить его самостоятельно. Но есть несколько теоретических ограничений, которые вы можете рассмотреть заранее:

    • В случае вложенного словаря, наличие дополнительного словаря для каждого ключа (внешнего и внутреннего) будет иметь некоторые издержки памяти (более того, что создавало бы кортеж).

    • В случае вложенного словаря каждое базовое действие, такое как сложение, обновление, поиск, удаление и т. Д., Должно выполняться в двух словарях. Теперь есть случай, когда вложенный словарьный подход может быть более быстрым, т. Е. Когда просматриваемые данные отсутствуют, поскольку промежуточные словари могут обойти полное вычисление и сравнение hash-кода, но затем снова нужно быть уверенным. При наличии данных он должен быть медленнее, так как поиск должен выполняться дважды (или трижды в зависимости от вложенности).

    • Что касается подхода кортежа, то кортежи .NET не являются наиболее эффективными, когда они предназначены для использования в качестве ключей в наборах, поскольку его реализация Equals и GetHashCode вызывает бокс для типов значений .

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


    На стороне примечание, немного косметики могут сделать словарь прохладным:

    1. Вызов стиля индексатора может быть намного чище и интуитивно понятным. Например, например,

       string foo = dict[a, b, c]; //lookup dict[a, b, c] = ""; //update/insertion 

      Поэтому выставляйте необходимые индексы в своем словаре, который внутренне обрабатывает вставки и поисковые запросы.

    2. Кроме того, реализуйте подходящий интерфейс IEnumerable и предоставляйте метод Add(TypeA, TypeB, TypeC, string) который даст вам синтаксис инициализатора коллекции, например:

       new MultiKeyDictionary { { a, b, c, null }, ... }; 

    Хороший, чистый, быстрый, легкий и понятный способ:

    просто сделайте простой class ключей, полученный из кортежа .

    добавьте что-то подобное:

     public sealed class myKey : Tuple { public myKey(TypeA dataA, TypeB dataB, TypeC dataC) : base (dataA, dataB, dataC) { } public TypeA DataA { get { return Item1; } } public TypeB DataB { get { return Item2; } } public TypeC DataC { get { return Item3; } } } 

    Таким образом, вы можете использовать его со словарем:

     var myDictinaryData = new Dictionary() { {new myKey(1, 2, 3), "data123"}, {new myKey(4, 5, 6), "data456"}, {new myKey(7, 8, 9), "data789"} }; 
    • Вы также можете использовать его в контрактах
    • как ключ для объединения или группировки в linq
    • таким образом, вы никогда не ошибаетесь в порядке вещей Item1, Item2, Item3 …
    • вам не нужно помнить или заглядывать в код, чтобы понять, куда идти, чтобы получить что-то
    • нет необходимости переопределять IStructuralEquatable, IStructuralComparable, IComparable, ITuple, они все здесь

    Если по какой-то причине вы действительно хотите избежать создания собственного classа Tuple или используя встроенный .NET 4.0, возможен еще один подход; вы можете объединить три ключевых значения вместе в одно значение.

    Например, если три значения являются целыми типами вместе, не принимая более 64 бит, вы можете объединить их в ulong .

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

     string.Format("{0}#{1}#{2}", key1, key2, key3) 

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

    Я бы перегрузил ваш Tuple надлежащим GetHashCode и просто использовал его как ключ.

    Пока вы перегружаете правильные методы, вы должны видеть приличную производительность.

    Вот тэг .NET для справки:

     [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITuple { private readonly T1 m_Item1; private readonly T2 m_Item2; private readonly T3 m_Item3; public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } public T3 Item3 { get { return m_Item3; } } public Tuple(T1 item1, T2 item2, T3 item3) { m_Item1 = item1; m_Item2 = item2; m_Item3 = item3; } public override Boolean Equals(Object obj) { return ((IStructuralEquatable) this).Equals(obj, EqualityComparer.Default);; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) { if (other == null) return false; Tuple objTuple = other as Tuple; if (objTuple == null) { return false; } return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3); } Int32 IComparable.CompareTo(Object obj) { return ((IStructuralComparable) this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) { if (other == null) return 1; Tuple objTuple = other as Tuple; if (objTuple == null) { throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleIncorrectType", this.GetType().ToString()), "other"); } int c = 0; c = comparer.Compare(m_Item1, objTuple.m_Item1); if (c != 0) return c; c = comparer.Compare(m_Item2, objTuple.m_Item2); if (c != 0) return c; return comparer.Compare(m_Item3, objTuple.m_Item3); } public override int GetHashCode() { return ((IStructuralEquatable) this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) { return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3)); } Int32 ITuple.GetHashCode(IEqualityComparer comparer) { return ((IStructuralEquatable) this).GetHashCode(comparer); } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("("); return ((ITuple)this).ToString(sb); } string ITuple.ToString(StringBuilder sb) { sb.Append(m_Item1); sb.Append(", "); sb.Append(m_Item2); sb.Append(", "); sb.Append(m_Item3); sb.Append(")"); return sb.ToString(); } int ITuple.Size { get { return 3; } } } 

    Если ваш потребительский код может работать с интерфейсом IDictionary <>, вместо словаря, мой инстинкт должен был бы использовать SortedDictionary <> с помощью специализированного сопоставления массива, то есть:

     class ArrayComparer : IComparer> where T : IComparable { public int Compare(IList x, IList y) { int compare = 0; for (int n = 0; n < x.Count && n < y.Count; ++n) { compare = x[n].CompareTo(y[n]); } return compare; } } 

    И создайте таким образом (используя int [] только для конкретного примера):

     var dictionary = new SortedDictionary(new ArrayComparer()); 
    Interesting Posts

    Objective-C – CABasicAnimation, применяющая изменения после анимации?

    Как увеличить разрешение существующего изображения в Gimp?

    Загрузка изображений с использованием Node.js, Express и Mongoose

    Как запросить XML с помощью пространств имен в Java с XPath?

    Как определить, находится ли точка в правой или левой части строки

    Найти дубликаты определенного файла

    Рекомендации по хранению почтовых адресов в базе данных (РСУБД)?

    Отключен ли SQL Server Compact с Visual Studio 2013?

    Android GCM: тот же идентификатор отправителя для большего количества приложений

    jQuery загрузка изображений с полным обратным вызовом

    изменения хостов Google в URL-адресе

    Анимированная иконка для ActionItem

    Как импортировать CSV-файлы в таблицу PostgreSQL?

    Сжатые папки NTFS: можно ли настроить коэффициент сжатия?

    Почему мой удаленный процесс все еще работает после убийства сеанса ssh?

    Давайте будем гением компьютера.