Энтони Пеграм сказал это лучше всего. Используйте правильный инструмент для работы. Я говорю это потому, что Distinct
или HashSet
не так уж сильно отличаются, когда речь идет о производительности. Используйте HashSet
, когда коллекция всегда должна содержать только разные вещи. Это также говорит программисту, что вы не можете добавлять к нему дубликаты. Используйте обычные List<T>
и .Distinct()
, когда вам нужно будет добавить дубликаты и удалить дубликаты позже. Намерение имеет значение.
В общем
a) HashSet может не принести пользы, если вы добавляете новые объекты из db и не указали свой собственный Equals
. Каждый объект из db может быть новым экземпляром для вашего хэш-набора (если вы только новичок), и это приведет к дублированию в коллекции. В этом случае используйте обычный List<T>
.
b) Если у вас есть определитель сравнения, определенный для hashset, и ваша коллекция всегда должна содержать только отдельные объекты, используйте hashset.
в) Если у вас есть определитель сравнения, определенный для хэш-набора, и вы хотите, чтобы из базы данных были только отдельные объекты, но коллекция не всегда должна содержать только отдельные объекты (т. Е. Дубликаты должны быть добавлены позже), более быстрый подход заключается в получении элементы из базы данных в хэш-набор и затем возвращают обычный список из этого хэш-набора.
d) Лучшее, что вы должны сделать, - это поставить задачу удаления дубликатов в базу данных, это правильный инструмент И это первый класс!
Что касается различий в производительности, в моем тестировании я всегда обнаруживал, что HashSet работает быстрее, но это только маргинал. Это очевидно, учитывая, что с подходом List вы должны сначала добавить, а затем сделать отличительный от него.
Метод испытания: начиная с двух основных функций,
public static void Benchmark(Action method, int iterations = 10000)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < iterations; i++)
method();
sw.Stop();
MsgBox.ShowDialog(sw.Elapsed.TotalMilliseconds.ToString());
}
public static List<T> Repeat<T>(this ICollection<T> lst, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count");
var ret = Enumerable.Empty<T>();
for (var i = 0; i < count; i++)
ret = ret.Concat(lst);
return ret.ToList();
}
Реализация:
var d = Enumerable.Range(1, 100).ToList().Repeat(100);
HashSet<int> hash = new HashSet<int>();
Benchmark(() =>
{
hash.Clear();
foreach (var item in d)
{
hash.Add(item);
}
});
~ 3300 мс
var d = Enumerable.Range(1, 100).ToList().Repeat(100);
List<int> list = new List<int>();
Benchmark(() =>
{
list.Clear();
foreach (var item in d)
{
list.Add(item);
}
list = list.Distinct().ToList();
});
~ 5800 мс
Разница в 2,5 секунды - это неплохо для списка из 10000 объектов при повторении еще 10000 раз. Для нормальных случаев разница вряд ли будет заметна.
Наилучший подход, возможно, для вас с вашим текущим дизайном:
var d = Enumerable.Range(1, 100).ToList().Repeat(100);
HashSet<int> hash = new HashSet<int>();
List<int> list = new List<int>();
Benchmark(() =>
{
hash.Clear();
foreach (var item in d)
{
hash.Add(item);
}
list = hash.ToList();
});
~ 3300 мс
Нет существенной разницы, см ..
Частично не связанный - после публикации этого ответа мне было любопытно узнать, каков наилучший подход к удалению дубликатов из обычного списка.
var d = Enumerable.Range(1, 100).ToList().Repeat(100);
HashSet<int> hash = new HashSet<int>();
List<int> list = new List<int>();
Benchmark(() =>
{
hash = new HashSet<int>(d);
});
~ 3900 мс
var d = Enumerable.Range(1, 100).ToList().Repeat(100);
List<int> list = new List<int>();
Benchmark(() =>
{
list = d.Distinct().ToList();
});
~ 3200 мс
Здесь правильный инструмент Distinct
быстрее, чем хакерский HashSet
! Возможно, это накладные расходы на создание хеш-набора.
Я тестировал различные другие комбинации, такие как ссылочные типы, без дубликатов в исходном списке и т. Д. Результаты согласуются.