我有很多代码可以用来做这样的事情
bool GetIsUnique(IEnumerable<T> values)
{
return values.Count() == values.Distinct().Count;
}
有更好的更快更好的方法吗?
答案 0 :(得分:21)
我会把它作为一个很好的扩展方法
public static bool IsUnique<T>(this IEnumerable<T> list)
{
var hs = new HashSet<T>();
return list.All(hs.Add);
}
检查是否可以将所有项目添加到HashSet。
答案 1 :(得分:19)
您的方法需要迭代序列两次,但有一些潜在的缺点:
Count
,每次都需要迭代整个序列。一旦你知道重复价值,就没有理由不提前爆发。以下方法只需要遍历序列一次,并且一旦遇到任何重复值就会提前中断:
bool GetIsUnique<T>(IEnumerable<T> values)
{
var set = new HashSet<T>();
foreach (T item in values)
{
if (!set.Add(item))
return false;
}
return true;
}
答案 2 :(得分:5)
我认为如果有非唯一值,这取决于你想要做什么。 @Jamiec's Or @ LukeH的答案是很好的答案,可能最适合纯粹的速度,但它无法告诉你问题在哪里。
您可能还会考虑类似
的内容var group = values.GroupBy(x => x);
return group.Any(g => g.Count() > 1);
它本身比HashSet
实施更糟糕。但如果你保持这个群体,你可以找到哪些元素是重复的。
var group = values.GroupBy(x => x);
return group.Where(g => g.Count() > 1);
或者
var group = values.GroupBy(x => x);
return group.Where(g => g.Count() > 1).Select(g => g.Key);
使用GroupBy
进行思考可让您保持选择权,以便下一步做什么。但如果你关心的只是知道所有的值是否都是唯一的,我会选择HashSet
答案 3 :(得分:1)
你将通过以上数据进行两次循环 - 一次获得计数,一次获得非常计数。如果前两个项目相同则特别糟糕!尝试这样的事情:
bool GetIsUnique<T>(IEnumerable<T> values)
{
HashSet<T> hashSet = new HashSet<T>();
foreach(var value in values)
{
if (hashSet.Contains(value))
{
return false;
}
hashSet.Add(value);
}
return true;
}
这一个会在找到重复后立即完成。显然它在哈希查找的速度上,但鉴于Distinct在内部使用了一个集合,我仍然期望它更快。
答案 4 :(得分:0)
两个基本规则:
其他方法会更快(当找到重复值时它们会短路,返回false),但是,如果它是我的代码,我仍然坚持你的版本。
答案 5 :(得分:0)
找到第一个副本的快速方法(如果存在)是:
public static bool TryFindFirstDuplicate<T>(this IEnumerable<T> source, out T duplicate)
{
var set = new HashSet<T>();
foreach (var item in source)
{
if (!set.Add(item))
{
duplicate = item;
return true;
}
}
duplicate = default(T);
return false;
}
答案 6 :(得分:0)
我很惊讶还没有人对此进行过测试:
将问题中的Gluip版本与JamieC,LukeK和MrKWatkins进行比较,这三个答案都比问问者版本更好。
在三个答案中,它们都是相当可比的,但在大多数情况下,JamieC的速度稍快一些。
当数据没有重复项或IEnumerable末尾有重复项时,大小或算法没有重大差异。
当数据在中间附近或开始时有重复项时,原始问题中的Gluip版本显示出其慢于其他三个。
检查集合的时间似乎与集合的大小呈线性比例关系,这意味着对于大或小的集合,最好不要使用任何算法。
这是一个测试程序,可以输出CSV,您可以将CSV加载到电子表格程序中,并根据需要进行排序和绘制图形:
测试程序:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AreUniqueTest
{
class Program
{
const int Iterations = 1000;
enum DupeLocation
{
None,
Early,
Center,
Late,
}
enum SetSize
{
Tiny= 10,
Small = 100,
Medium = 1000,
Large = 10000,
Huge = 100000,
}
static void Main()
{
Dictionary<string, Func<IEnumerable<int>, bool>> functions = new Dictionary<string, Func<IEnumerable<int>, bool>>
{
{"Gluip", GetIsUniqueGluip},
{"LukeH", GetIsUniqueLukeH },
{"Jamiec", GetIsUniqueJamiec },
{"MrKWatkins", GetIsUniqueMrKWatkins }
};
var output = new StringBuilder();
Console.WriteLine("Function,SetSize,DupeLocation,TotalTicks,AverageTicks");
output.AppendLine("Function,SetSize,DupeLocation,TotalTicks,AverageTicks");
foreach (SetSize size in Enum.GetValues(typeof(SetSize)))
{
var sizevalue = (int) size;
foreach (DupeLocation location in Enum.GetValues(typeof(DupeLocation)))
{
var data = CreateTestData((int)size, location);
foreach (string functionKey in functions.Keys)
{
var ticks = RunSet(functions[functionKey], data, Iterations);
var avg = ticks / Iterations;
Console.WriteLine($"{functionKey},{sizevalue},{location},{ticks},{avg}");
output.AppendLine($"{functionKey},{sizevalue},{location},{ticks},{avg}");
}
}
}
File.WriteAllText("output.csv", output.ToString());
Process.Start("output.csv");
}
static long RunSet<T>(Func<IEnumerable<T>, bool> getIsUnique, IEnumerable<T> values, int iterations)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i < iterations; i++)
{
getIsUnique.Invoke(values);
}
stopwatch.Stop();
return stopwatch.ElapsedTicks;
}
static bool GetIsUniqueLukeH<T>(IEnumerable<T> values)
{
var set = new HashSet<T>();
foreach (T item in values)
{
if (!set.Add(item))
return false;
}
return true;
}
static bool GetIsUniqueGluip<T>(IEnumerable<T> values)
{
return values.Count() == values.Distinct().Count();
}
static bool GetIsUniqueJamiec<T>(IEnumerable<T> list)
{
var hs = new HashSet<T>();
return list.All(hs.Add);
}
static bool GetIsUniqueMrKWatkins<T>(IEnumerable<T> values)
{
HashSet<T> hashSet = new HashSet<T>();
foreach (var value in values)
{
if (hashSet.Contains(value))
{
return false;
}
hashSet.Add(value);
}
return true;
}
static int[] CreateTestData(int size, DupeLocation location)
{
var result = new int[size];
Parallel.For(0, size, i => { result[i] = i; });
return SetDupe(result, location);
}
static int[] SetDupe(int[] values, DupeLocation location)
{
switch (location)
{
case DupeLocation.Early:
values[1] = values[0];
break;
case DupeLocation.Center:
var midpoint = values.Length / 2;
values[midpoint] = values[midpoint + 1];
break;
case DupeLocation.Late:
values[values.Length - 1] = values[values.Length - 2];
break;
// case DupeLocation.None: // do nothing.
}
return values;
}
}
}