Быстрые способы избежать дубликатов в списке <> в C #

Моя программа C# генерирует случайные строки из заданного шаблона. Эти строки хранятся в списке. Поскольку дубликатов не разрешено, я делаю это так:

List myList = new List();
for (int i = 0; i < total; i++) {
  string random_string = GetRandomString(pattern);
  if (!myList.Contains(random_string)) myList.Add(random_string);
}

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

Есть ли более быстрые способы избежать дубликатов?

21
@Jonesy: Это похоже на то, что нужно проверить для определенного набора данных. Если это будет быстрее, тогда можно было бы взвесить эту оптимизацию производительности против обфускации, которую она добавляет к коду (что в этом случае не так уж и много).
добавлено автор David, источник
было бы быстрее добавить их все, а затем использовать Distinct() для проверки дубликатов, а затем добавить обратно номер, который был удален?
добавлено автор Jonesopolis, источник
Просто из интереса, для чего именно вы их используете?
добавлено автор musefan, источник
@Servy: Достаточно справедливо, вы, скорее всего, правы, это, безусловно, звучит логично в любом случае
добавлено автор musefan, источник
@Servy: Зависит от вероятности конфликта. Если программе необходимо загрузить список из БД, это может быть приемлемым компромиссом.
добавлено автор musefan, источник
Если вы сохраняете свой список в базе данных, вы также можете попытаться сделать поле уникальным, а затем, если INSERT не удастся, вы можете попробовать другое - просто что-то еще, чтобы рассмотреть
добавлено автор musefan, источник
@Servy Нет, к сожалению. Этот шаблон является особым, поэтому GUID не помогут.
добавлено автор Robert Strauch, источник
@musefan Мне нужны эти, чтобы генерировать серийные номера для документов.
добавлено автор Robert Strauch, источник
@musefan Выполняя всю поездку в оба конца DB, чтобы узнать, что строка уже существует, будет ... проблемой.
добавлено автор Servy, источник
@musefan Выполнение даже одного запроса БД, чтобы определить, будет ли элемент, уже существующий в БД, занимать дольше сотни тысяч, если не миллионы проверок, чтобы увидеть, существует ли элемент в хеше в памяти. Использование БД для решения этой конкретной проблемы может быть в несколько тысяч раз ниже.
добавлено автор Servy, источник
@Robert Не могли бы вы использовать GUID GUID для каждого документа?
добавлено автор Servy, источник
использовать для избежания дублирования
добавлено автор Jayram Singh, источник
@David Я бы предположил теоретический аргумент, что HashSet будет быстрее из-за меньшего влияния на память, и после этого не потребуется итерации. Стоимость проверки каждого элемента все еще существует, но структура данных оптимизирована для него.
добавлено автор Adam Houldsworth, источник

7 ответы

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

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

35
добавлено
Итак, я использовал HashSet , и увеличение скорости огромно. Однако у меня есть новая проблема. Мне нужно определенное количество записей в хэш-наборе. Если я использую for-loop, как в моем вопросе, то он останавливается после 2 000 000 циклов. Дубликаты не существуют в хэш-наборе, но если дубликат попал, хэш-набор не имеет 2 000 000 записей. Как я мог избежать этого? if (myList.Count <2000000) myList.Add (random_string); предотвращает это, но опять-таки медленно.
добавлено автор Robert Strauch, источник
@Robert Вместо for (int i = 0; i просто используйте for (int i = 0; set.Count . Или, если вам вообще не нужен i , тогда просто while (set.Count .
добавлено автор Servy, источник
кажется, что поиск элемента для HasSet равен O (1), поэтому, если вы найдете этот элемент = добавьте его в дублирующий список.
добавлено автор user2545071, источник

Don't use List<>. Use Dictionary<> or HashSet<> instead!

9
добавлено
Используя HashSet, вы НЕ МОЖЕТЕ получить доступ и изменить объект, как вы можете, со списком.
добавлено автор ppumkin, источник

Самый простой способ - использовать это:

myList = myList.Distinct().ToList();

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

public IEnumerable GetRandomStrings(int total, string pattern)
{
    for (int i = 0; i < total; i++) 
    {
        yield return GetRandomString(pattern);
    }
}

...

myList = GetRandomStrings(total, pattern).Distinct().ToList();

Конечно, если вам не нужно обращаться к элементам по индексу, вы, вероятно, могли бы повысить эффективность еще больше, сбросив ToList и просто используя IEnumerable .

5
добавлено
Использование .Distinct для удаления нескольких миллионов строк в списке не означает, что эффективная IMO.
добавлено автор Darren Davies, источник
Кроме того, если в результате есть некоторое количество строк, это может иметь смысл иметь GetRandomStrings генерировать бесконечно длинную последовательность, а затем использовать Take , чтобы ограничить ее желаемый размер. Затем вы можете поместить Take до или после Distinct , в зависимости от того, хотите ли вы указать количество созданных строк или число unique генерируемые строки.
добавлено автор Servy, источник
@ p.s.w.g Я предполагаю, что ваш метод GetRandomStrings предназначен для yield строки, а не только для локализации, а затем выбросить его.
добавлено автор Servy, источник
@DarrenDavies Internally, Distinct использует HashSet , как и другие. Единственная неэффективная часть - сначала генерирует список, а затем использует различные, которые я рассмотрел во второй части моего ответа.
добавлено автор p.s.w.g, источник
@Servy Да, спасибо.
добавлено автор p.s.w.g, источник
@Servy Я изначально реализовал это, но бесконечные генераторы могут быть опасными, и с ними нужно обращаться с некоторой осторожностью.
добавлено автор p.s.w.g, источник

You could use a HashSet if order is not important:

HashSet myHashSet = new HashSet();
for (int i = 0; i < total; i++) 
{
   string random_string = GetRandomString(pattern);
   myHashSet.Add(random_string);
}

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

MSDN

Или если порядок важен, я бы рекомендовал использовать SortedSet (только для .net 4.5)

5
добавлено
Как мне получить хешированный объект? У HashSet нет GET, и это не очень эффективно для реализации вашего.
добавлено автор ppumkin, источник
Обратите внимание, что SortedSet сортирует элементы. Если требуется упорядоченный набор (т. Е. Порядок элементов поддерживается) OrderedDictionary будет лучшим выбором. Недостатком является то, что он не является общим.
добавлено автор Olivier Jacot-Descombes, источник

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

bool containsKey;
string newKey;

    public void addKey(string newKey){

         foreach(string key in MyKeys){
           if(key == newKey){
             containsKey = true;
          }
         }

      if(!containsKey){
       MyKeys.add(newKey);
     }else{
       containsKey = false;
     }

    }
1
добавлено

Hashtable был бы более быстрым способом проверить, существует ли элемент, чем список.

0
добавлено
У него нет отношения ключ/значение, просто куча строк, поэтому ему нужен набор, а не карта. Кроме того, HashTable не является общим; вы должны использовать общий Dictionary , если вам действительно нужна структура карты. Вы никогда не должны использовать HashTable в не устаревшем коде.
добавлено автор Servy, источник

Ты пробовал:

myList = myList.Distinct()
0
добавлено
Верстка сайтов HTML/CSS/JS/PHP
Верстка сайтов HTML/CSS/JS/PHP
3 439 участник(ов)

Правила группы: напишите !rules в чате. Группа Вк: vk.com/web_structure Freelancer: @web_fl Веб Дизайн: @dev_design Маркетолог: @topmarkening Автор: @M_Boroda

Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

CSS — русскоговорящее сообщество
CSS — русскоговорящее сообщество
1 502 участник(ов)

Сообщество любителей CSS Возникли проблемы с CSS? – пиши сюда, обсудим и предложим самое лучшее решение Работа: @css_ru_jobs Правила: https://teletype.in/@css_ru/r1EWtQ2w7 Приходите в наши чаты @javascript_ru и @frontend_ru Флуд: @css_flood

Чат — Типичный Верстальщик
Чат — Типичный Верстальщик
1 080 участник(ов)

Основной канал: @tpverstak Обратная связь: @annblok Все ссылки на соц.сети проекта: http://taplink.cc/tpverstak ПРАВИЛА ЧАТА — https://teletype.in/@annblok/BygPgC3E7

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot

Веб-Технологи: UI/UX, Вёрстка, Фронтенд
Веб-Технологи: UI/UX, Вёрстка, Фронтенд
167 участник(ов)

Всё про веб-дизайн и вёрстку. А также: HTML, CSS, флекс и бутстрапы, шаблонизаторы, препроцессоры, методологии, аглифаеры, улучшаторы и обфускаторы. Обсуждаем темы юзабилити, устраиваем А/В тесты лендингов, и проводим аудит.

DTP :: @DTPublish
DTP :: @DTPublish
147 участник(ов)

Обсуждаемые темы: полиграфия, препресс, верстка, дизайн, иллюстрации, скрипты, плагины. Канал - @DTPublishing

css_jobs
css_jobs
26 участник(ов)

Чат для вопросов по css и html: @css_ru Флуд: @css_flood Канал с вакансиями и резюме: @css_jobs_feed

css_флуд
css_флуд
10 участник(ов)