Похоже, что в .NET 3.5 нет общей реализации OrderedDictionary
(которая находится в System.Collections.Specialized
пространстве имен). Я скучаю по одному?
Я нашел реализации для обеспечения функциональности, но удивился, если / почему не существует универсальной реализации «из коробки», и если кто-нибудь знает, что-то в .NET 4.0?
OrderedDictionary<T>
: codeproject.com/Articles/18615/…Systems.Collections.Generic
. Давайте запросимOrderedDictionary<TKey,TValue>
.NET 5. Как уже отмечали другие, случай, когда ключ является целым, является вырожденным и потребует особого внимания.Ответы:
Ты прав. Там нет общего эквивалента
OrderedDictionary
в самой структуре.(Насколько я знаю, это относится и к .NET 4).
Но вы можете проголосовать за него в UserVoice Visual Studio (2016-10-04)!
источник
Реализация универсального
OrderedDictionary
не очень сложно, но это излишне много времени и, честно говоря, этот класс является огромным упущением со стороны Microsoft. Есть несколько способов реализовать это, но я решил использоватьKeyedCollection
для своего внутреннего хранилища. Я также решил реализовать различные методы сортировки,List<T>
поскольку это по сути является гибридом IList и IDictionary. Я включил мою реализацию здесь для потомков.Вот интерфейс. Обратите внимание, что он включает
System.Collections.Specialized.IOrderedDictionary
, который является неуниверсальной версией этого интерфейса, предоставленной Microsoft.Вот реализация вместе с вспомогательными классами:
И ни одна реализация не была бы полной без нескольких тестов (но, к сожалению, SO не позволит мне выложить столько кода в одном посте), поэтому мне придется оставить вас, чтобы писать ваши тесты. Но я оставил несколько из них, чтобы вы могли понять, как это работает:
-- ОБНОВИТЬ --
Источник этого и других действительно полезных отсутствующих базовых библиотек .NET здесь: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs
источник
TKey
естьint
? Какthis[]
будет работать в таком случае?Для записи существует универсальная KeyedCollection, которая позволяет индексировать объекты с помощью int и ключа. Ключ должен быть встроен в значение.
источник
Вот странная находка: пространство имен System.Web.Util в System.Web.Extensions.dll содержит универсальный OrderedDictionary
Не уверен, почему MS поместил его туда вместо пакета System.Collections.Generic, но я предполагаю, что вы можете просто скопировать вставить код и использовать его (он внутренний, поэтому не может использовать его напрямую). Похоже, что реализация использует стандартный словарь и отдельные списки Key / Value. Довольно просто ...
источник
System.Runtime.Collections
также содержит внутреннее,OrderedDictionary<TKey, TValue>
которое просто обтекает неуниверсальную версиюSystem.Web.Util.OrderedDictionary<TKey, TValue>
внутренне реализовано вокруг универсальногоDictionary
. Как ни странно, это не реализуется,IList
ноICollection<KeyValuePair<TKey, TValue>>
List<KeyValuePair<TKey,TValue>>
будет конкурентоспособным из-за схемы доступа к памяти, для больших размеров просто используйте тот же список +Dictionary<TKey,int>
в качестве поиска. AFAIK, в BigO нет такой структуры данных, которая лучше справляется с точки зрения скорости и памяти.System.Collections.Specialized.OrderedDictionary
не является универсальным типом. Посмотрите, ма, на странице документа, на которую выДля чего это стоит, вот как я это решил:
Это можно инициализировать так:
и доступны так:
источник
Add
методов.pairList.Add(new KeyValuePair<K,V>())
(т. Е. МетодList
класса). В этом случаеitsIndex
словарь не обновляется, и теперь список и словарь не синхронизированы. Может скрытьList.Add
метод путем созданияnew
PairList.Add(KeyValuePair<K,V>)
метода или использовать композицию вместо наследования и простоList
снова реализовать все методы ... намного больше кода ...if (itsindex.Contains(key)) return;
в начало,Add
чтобы предотвратить дублированиеОсновная концептуальная проблема с общей версией
OrderedDictionary
состоит в том, что пользователиOrderedDictionary<TKey,TValue>
ожидают, что смогут индексировать ее численно с помощьюint
или путем поиска с помощьюTKey
. Когда единственным типом ключа былObject
, как в случае с неуниверсальнымOrderedDictionary
, тип аргумента, передаваемый индексатору, было бы достаточно, чтобы отличить, какой тип операции индексации должен быть выполнен. Тем не менее, неясно, какOrderedDictionary<int, TValue>
должен вести себя индексатор .Если классы like
Drawing.Point
рекомендовали и следовали правилу, согласно которому кусочно-изменяемые структуры должны предоставлять свои изменяемые элементы в виде полей, а не свойств, и воздерживаться от использования модификаторов свойств, которые изменяютthis
, тогда ониOrderedDictionary<TKey,TValue>
могли бы эффективно предоставлятьByIndex
свойство, которое возвращалоIndexer
структуру, которая содержала ссылку на словарь и имел индексированное свойство, геттер и сеттер назвал быGetByIndex
иSetByIndex
на него. Таким образом, можно сказать что-то вродеMyDict.ByIndex[5] += 3;
добавления 3 к шестому элементу словаря.К сожалению, чтобы компилятор мог принять такую вещь, было бы необходимо заставить
ByIndex
свойство возвращать новый экземпляр класса, а не структуру при каждом вызове, исключая преимущества, которые можно было бы получить, избегая бокса.В VB.NET эту проблему можно обойти, используя именованное индексированное свойство (
MyDict.ByIndex[int]
то есть членMyDict
, а неMyDict.ByIndex
обязательный член,MyDict
включающий индексатор), но C # не допускает таких вещей.Возможно, все еще стоило бы предложить
OrderedDictionary<TKey,TValue> where TKey:class
, но во многом причина предоставления обобщений в первую очередь заключалась в том, чтобы разрешить их использование с типами значений.источник
int
ключи с типами представляют проблему, но этого можно избежать, следуя примеру связанногоSortedList<TKey, TValue>
типа: поддержка ключей только с помощью[...]
и использование.Values[...]
для доступа по индексу. (SortedDictionary<TKey, TValue>
напротив, который не оптимизирован для индексированного доступа, требует использования.ElementAt(...).Value
)Для многих целей я нашел, что можно обойтись с
List<KeyValuePair<K, V>>
. (Нет, если вам нужно расширить егоDictionary
, очевидно, и нет, если вам нужен лучший, чем O (n) поиск по значению ключа.)источник
Tuple<T1, T2>
если они не имеют отношения ключ-значение.Есть
SortedDictionary<TKey, TValue>
. Хотя семантически близко, я не утверждаю, что это то же самое, чтоOrderedDictionary
просто потому, что это не так. Даже из характеристик производительности. Тем не менее, очень интересное и довольно важное различие междуDictionary<TKey, TValue>
(и в той степениOrderedDictionary
и реализациями, представленными в ответах) иSortedDictionary
в том, что последний использует двоичное дерево внизу. Это критическое различие, потому что оно делает класс невосприимчивым к ограничениям памяти, применяемым к универсальному классу. Посмотрите эту ветку оOutOfMemoryExceptions
броске, когда универсальный класс используется для обработки большого набора пар ключ-значение.Как определить максимальное значение параметра емкости, передаваемого в конструктор Dictionary, чтобы избежать исключения OutOfMemoryException?
источник
SortedDictionary
в порядке их добавления ? Вот чтоOrderedDictionary
позволяет. Отличная концепция, чем отсортированная . (Я делал эту ошибку в прошлом; я думал, что предоставление конструктора Comparer для OrderedDictionary сделает его сортированным, но все, что он делает с Comparer, это определяет равенство ключей; например, сравнение с нечувствительной к строковым значениям разрешает поиск с учетом нечувствительной к строковым значениям.)Да, это неудачное упущение. Я скучаю по Python's OrderedDict
Поэтому я написал свой собственный
OrderedDictionary<K,V>
класс на C #. Как это работает? Он поддерживает две коллекции - ванильный неупорядоченный словарь и упорядоченный список ключей. Благодаря этому решению стандартные словарные операции сохраняют свою быструю сложность, и поиск по индексу также выполняется быстро.https://gist.github.com/hickford/5137384
Вот интерфейс
источник
В продолжение комментария от @VB вот доступная реализация System.Runtime.Collections.OrderedDictionary <,> . Изначально я собирался получить доступ к нему с помощью рефлексии и предоставить его через фабрику, но DLL, в которой он находится, кажется не совсем доступным, поэтому я просто взял сам источник.
Стоит отметить, что индексатор здесь не будет выбрасывать
KeyNotFoundException
. Я абсолютно ненавижу это соглашение, и это была 1 свобода, которую я взял в этой реализации. Если это важно для вас, просто замените строку дляreturn default(TValue);
. Использует C # 6 ( совместимо с Visual Studio 2013 )Запросы на извлечение / обсуждение принимаются на GitHub
источник
Для тех, кто ищет «официальный» вариант пакета в NuGet, в .NET CoreFX Lab была принята реализация универсального OrderedDictionary. Если все пойдет хорошо, тип будет одобрен и интегрирован в основной репозиторий .NET CoreFX.
Существует вероятность того, что эта реализация будет отклонена.
Ссылку на принятую реализацию можно найти здесь https://github.com/dotnet/corefxlab/blob/57be99a176421992e29009701a99a370983329a6/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/OrderedDictionary.cs
Пакет NuGet, который определенно имеет этот тип, доступный для использования, можно найти здесь https://www.nuget.org/packages/Microsoft.Experimental.Collections/1.0.6-e190117-3
Или вы можете установить пакет в Visual Studio. Найдите пакет «Microsoft.Experimental.Collections» и убедитесь, что установлен флажок «Включить предварительный выпуск».
Обновлю этот пост, если и когда тип станет официально доступным.
источник
Я реализовал общее
OrderedDictionary<TKey, TValue>
, обтеканиеSortedList<TKey, TValue>
и добавление частногоDictionary<TKey, int>
_order
. Затем я создал внутреннюю реализациюComparer<TKey>
, передав ссылку на словарь _order. Затем я использую этот компаратор для внутреннего SortedList. Этот класс сохраняет порядок элементов, передаваемых конструктору, и порядок дополнений.Эта реализация имеет почти те же большие характеристики O, что
SortedList<TKey, TValue>
и добавление и удаление в _order - O (1). Каждый элемент займет (в соответствии с книгой «C # 4 в двух словах», стр. 292, таблица 7-1) дополнительное пространство памяти, равное 22 (накладные расходы) + 4 (порядок int) + размер TKey (допустим, 8) = 34 Вместе с дополнительнымиSortedList<TKey, TValue>
издержками в два байта общая нагрузка составляет 36 байтов, в то время как в той же книге говорится, что неуниверсальныеOrderedDictionary
издержки имеют 59 байтов.Если я
sorted=true
перейду к конструктору, то _order вообще не используется,OrderedDictionary<TKey, TValue>
это точноSortedList<TKey, TValue>
с незначительными накладными расходами для переноса, если вообще имеет смысл.Я собираюсь хранить не так много больших ссылочных объектов в
OrderedDictionary<TKey, TValue>
, так что для меня это ок. Накладные расходы 36 байтов допустимы.Основной код ниже. Полный обновленный код находится на этой сути .
источник
OrderedDictionary
отношении вставок или удалений: (1) никогда не будет никаких удалений; (2) будут исключения, но важно, чтобы элементы перечислялись в порядке добавления; нет необходимости доступа по индексу; (3) числовой индекс элемента должен (или, по крайней мере, может) оставаться постоянным, и в течение срока действия коллекции будет добавлено не более 2 миллиардов элементов, поэтому, если элемент № 7 будет удален, больше никогда не будет пункт № 7; (4) индекс предмета должен быть его рангом по отношению к выжившим.Dictionary
. Сценарии № 2 и № 3 могут быть обработаны, если каждый элемент поддерживает индекс, сообщающий, когда он был добавлен, и ссылки на элементы, добавленные до или после него. Сценарий № 4 является единственным, который, по-видимому, не сможет достичь производительности O (1) для операций в произвольной последовательности. В зависимости от моделей использования, № 4 может помочь при использовании различных стратегий ленивого обновления (вести учет в дереве и вносить изменения в узел как недействительный, а не обновлять узел и его родителей).