Производительность при проверке дубликатов

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

List<int>

и

Dictionary<int, bool>

Со словарем я обнаружил немного лучшую производительность, хотя мне никогда не нужно, чтобы логическое значение было помечено каждой записью. Я ожидаю, что это связано с тем, что список допускает индексированный доступ, а словарь - нет. Мне было интересно, есть ли лучшее решение этой проблемы. Мне не нужно снова обращаться к записям, мне нужно только отслеживать, какие «первичные ключи» я видел, и убедиться, что я выполняю дополнительную работу только с записями, имеющими новый первичный ключ. Я использую C# и .NET 2.0. И у меня нет контроля над исправлением входных данных для удаления дубликатов из источника (к сожалению!). Итак, вы можете почувствовать масштабирование, в целом я проверяю дубликаты примерно 1000000 раз в приложении, но в подмножествах не более 64000, которые должны быть уникальными.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
424
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Они добавили класс HashSet в .NET 3.5. Но я думаю, он будет на уровне Словаря. Если у вас меньше 100 элементов, List, вероятно, будет работать лучше.

HashSet - это именно то, что я хочу, к сожалению, мы ограничены .net 2.0, однако, используя ссылку @Rob о том, как заставить Linq работать в .net 2.0, я пытаюсь заставить HashSet работать в нашей среде.

Timothy Carter 19.09.2008 15:12

Я действительно не понимаю, о чем вы спрашиваете.

Во-первых, это прямо противоположно тому, что вы говорите. У словаря есть индексированный доступ (это хеш-таблица), а у de List - нет.

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

Я полагаю, что у вас есть данные, хранящиеся в другом типе данных, и вы сохраняете их в словаре. В этом случае вставка данных будет работать с двумя словарями.

foreach (int key in keys)
{
  if (!MyDataDict.ContainsKey(key))
  {
    if (!MyDuplicatesDict.ContainsKey(key))
      MyDuplicatesDict.Add(key);
  }
  else
    MyDataDict.Add(key); 
}

Обновлено: не обращайте внимания на мой комментарий. Я думал, вы говорите о C++. Понятия не имею, актуален ли мой пост в мире C# ..

Хеш-таблица могла бы быть немного быстрее. Двоичные деревья (это то, что используется в словаре) имеют тенденцию быть относительно медленными из-за способа доступа к памяти. Это особенно актуально, если ваше дерево становится очень большим.

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

Вы можете увидеть увеличение скорости в 10 раз, просто подключив простой распределитель пула в шаблон словаря. Afaik boost имеет компонент, который можно использовать напрямую.

Другой вариант: если вы знаете, что существует только 64000 записей в ваших целых числах, вы можете записать их в файл и создать для него идеальную хеш-функцию. Таким образом, вы можете просто использовать хеш-функцию для сопоставления целых чисел в диапазоне от 0 до 64 000 и индексации битового массива.

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

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

Для лучшей упаковки вы можете реализовать структуру данных растрового изображения (в основном массив, но каждый int в массиве представляет 32 int в ключевом пространстве, используя 1 бит на ключ). Таким образом, если ваше максимальное число составляет 1000000, вам понадобится всего ~ 30,5 КБ памяти для структуры данных.

Выполнение растрового изображения будет O (1) (за проверку), что трудно превзойти.

Некоторое время назад был вопрос по удаление дубликатов из массива. Для целей вопроса производительность не имела особого значения, но вы можете взглянуть на ответы, поскольку они могут дать вам некоторые идеи. Кроме того, я могу ошибаться здесь, но если вы пытаетесь удалить дубликаты из массива, тогда команда LINQ, такая как Перечислимый., может дать вам лучшую производительность, чем то, что вы пишете сами. Оказывается, есть способ получить LINQ работает на .NET 2.0, поэтому этот маршрут стоит изучить.

Если вы собираетесь использовать список, используйте BinarySearch:

 // initailize to a size if you know your set size
List<int> FoundKeys = new List<int>( 64000 );
Dictionary<int,int> FoundDuplicates = new Dictionary<int,int>();

foreach ( int Key in MyKeys )
{
   // this is an O(log N) operation
   int index = FoundKeys.BinarySearch( Key );
   if ( index < 0 ) 
   {
       // if the Key is not in our list, 
       // index is the two's compliment of the next value that is in the list
       // i.e. the position it should occupy, and we maintain sorted-ness!
       FoundKeys.Insert( ~index, Key );
   }
   else 
   {
       if ( DuplicateKeys.ContainsKey( Key ) )
       {
           DuplicateKeys[Key]++;
       }
       else
       {
           DuplicateKeys.Add( Key, 1 );
       }
   } 
} 

Вы также можете использовать это для любого типа, для которого вы можете определить IComparer с помощью перегрузки: BinarySearch (T item, IComparer <T>);

Другие вопросы по теме