Как создать HashSet> с различными элементами?

у меня есть HashSet, который содержит несколько списков целых чисел-т. е. HashSet<List<int>>

для поддержания уникальности мне в настоящее время приходится делать две вещи: 1. Вручную цикл, хотя существующие списки, ищет дубликаты с помощью SequenceEquals. 2. Сортировка отдельных списков так, что SequenceEquals в настоящее время работает.

есть ли лучший способ сделать это? Есть ли существующий IEqualityComparer, который я могу предоставить в HashSet, чтобы HashSet.Add() смогите автоматически отрегулировать уникальность?

var hashSet = new HashSet<List<int>>();

for(/* some condition */)
{
    List<int> list = new List<int>();

    ...

    /* for eliminating duplicate lists */

    list.Sort();

    foreach(var set in hashSet)
    {
        if (list.SequenceEqual(set))
        {
            validPartition = false;
            break;
        }
    }

    if (validPartition)
           newHashSet.Add(list);
}

спасибо !

4 ответов


вот возможный компаратор, который сравнивает IEnumerable<T> ее элементов. Перед добавлением по-прежнему необходимо выполнить сортировку вручную.

можно было бы встроить сортировку в компаратор, но я не думаю, что это мудрый выбор. Добавление канонической формы списка кажется более мудрым.

этот код будет работать только в .net 4, поскольку он использует преимущества общей дисперсии. Если вам нужны более ранние версии, вам нужно либо заменить IEnumerable С List, или добавьте второй общий параметр для тип коллекции.

class SequenceComparer<T>:IEqualityComparer<IEnumerable<T>>
{
    public bool Equals(IEnumerable<T> seq1,IEnumerable<T> seq2)
    {
        return seq1.SequenceEqual(seq2);
    }

    public int GetHashCode(IEnumerable<T> seq)
    {
        int hash=1234567;
        foreach(T elem in seq)
            hash=hash*37+elem.GetHashCode();
        return hash;
    }
}

void Main()
{
    var hashSet = new HashSet<List<int>>(new SequenceComparer<int>());

    List<int> test=new int[]{1,3,2}.ToList();
    test.Sort();
    hashSet.Add(test);

    List<int> test2=new int[]{3,2,1}.ToList();
    test2.Sort();       
    hashSet.Contains(test2).Dump();
}

это начинается неправильно, это должно быть HashSet<ReadOnlyCollection<>> потому что нельзя разрешить спискам изменять и аннулировать предикат set. Это позволяет вычислить хэш-код в O (n) при добавлении коллекции в набор. И тест O(n), чтобы проверить, находится ли он уже в наборе с очень необычным o(n^2) в худшем случае, если все хэши окажутся равными. Сохраните вычисленный хэш в коллекции.


есть ли причина, по которой вы не просто используете массив? int[] будет работать лучше. Также я предполагаю, что списки содержат дубликаты, иначе вы бы просто использовали наборы и не имели проблем.

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

Итак, в первый раз вы работаете на конкретном List<int>, вы должны генерировать хэш на основе упорядоченной последовательности чисел и кэшировать его. Тогда в следующий раз list сравнивается, можно использовать кэшированное значение. Я не уверен, как вы можете сделать это с помощью компаратора с моей головы (может быть, статического словаря?)- но вы могли бы реализовать List обертка, которая делает это легко.


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