как заставить HashSet перефразировать участников? - PullRequest
0 голосов
/ 08 июня 2018

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

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

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

Класс определения:

public class Test {
    public int N;
    public override string ToString() { return this.N.ToString(); }
    }
public class TestClassEquality: IEqualityComparer<Test> {
    public bool Equals(Test x, Test y) { return x.N == y.N; }
    public int GetHashCode(Test obj) { return obj.N.GetHashCode(); }
    }

Тестовый код:

    TestClassEquality eq = new TestClassEquality();
    HashSet<Test> hs = new HashSet<Test>(eq);
    Test a = new Test { N = 1 }, b = new Test { N = 2 };
    hs.Add(a);
    hs.Add(b);
    b.N = 1;
    string fmt = "Count = {0}; Values = {1}";
    Console.WriteLine(fmt, hs.Count, string.Join(",", hs));
    hs.IntersectWith(hs);
    Console.WriteLine(fmt, hs.Count, string.Join(",", hs));
    hs = new HashSet<Test>(hs, eq);
    Console.WriteLine(fmt, hs.Count, string.Join(",", hs));
    hs = new HashSet<Test>(new List<Test>(hs), eq);
    Console.WriteLine(fmt, hs.Count, string.Join(",", hs));

Вывод:

"Count: 2; Values: 1,1"
"Count: 2; Values: 1,1"
"Count: 2; Values: 1,1"
"Count: 1; Values: 1"

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

Действительно ли это необходимо или есть какой-то более простой способ сделать это?

Ответы [ 3 ]

0 голосов
/ 08 июня 2018

Нет другого способа, кроме воссоздания HashSet<>.К сожалению, конструктор HashSet<> имеет оптимизацию, так что если он создается из другого HashSet<>, он копирует хэш-коды ... Итак, мы можем обмануть:

hs = new HashSet<Test>(hs.Skip(0), eq);

hs.Skip(0) - это IEnumerable<>, а не HashSet<>.Это побеждает проверку HashSet<>.

Обратите внимание, что нет гарантии, что в будущем Skip() не будет реализовывать короткое замыкание в случае 0, что-то вроде:

if (count == 0)
{
    return enu;
}
else
{
    return count elements;
}

(см. Комментарий Липперта, ложная проблема)

"Ручной" способ сделать это:

var hs2 = new HashSet<Test>(eq);
foreach (var value in hs)
{
    hs2.Add(value);
}
hs = hs2;

Так что перечислите "вручную"и прочитал.

0 голосов
/ 08 июня 2018

Комментарий Лассе верен: вы обязаны по контракту HashSet не делать этого , поэтому спрашивать, что делать, когда вы делаете это, не для начала.Если вам больно, когда вы это делаете, прекратите это . Изменяемый объект не должен быть помещен в хеш-набор, если мутация вызовет изменение его хеш-значения, пока оно находится в наборе .Вы в заколке своего собственного изготовления.

Чтобы выбраться из этой заколки, вы можете:

  • Прекратить мутировать объекты, пока они находятся в хэш-наборе.Удалите их до того, как вы их мутируете, затем вставьте обратно.
  • Исправьте реализацию равенства и хэширования на объекте так, чтобы он был согласованным при всех мутациях.
  • Когда вы создаете хэш-набор,предоставьте собственный алгоритм хеширования / равенства, который не меняет своего мнения, когда объект мутирует.
  • Реализуйте свой собственный класс "set", который будет вести себя так, как вам нравится в этом сценарии.Это чрезвычайно сложно, поэтому будьте осторожны.(Существует причина, по которой это ограничение было создано в первую очередь!)
0 голосов
/ 08 июня 2018

Как вы видели, HashSet s не имеют дело с изменяемыми объектами, когда изменение объекта влияет на его хэш-код или равенство с другими объектами.Просто удалите его и добавьте заново:

hs.Remove(b);
b.N = 1;
hs.Add(b);
...