Как создать 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. вот хороший пример.