测试列表中的所有值是否唯一


90

我有一小部分字节,我想测试一下它们是否都是不同的值。例如,我有这个:

List<byte> theList = new List<byte> { 1,4,3,6,1 };

检查所有值是否不同的最佳方法是什么?


2
由于这是典型的课堂问题,我将回答一个问题。如果将其分类,您将如何处理?
ctrl-alt-delor

Answers:


168
bool isUnique = theList.Distinct().Count() == theList.Count();

只是好奇:这对空间和时间有何要求?
dtb

10
@dtb应该约为O(N)。当然,考虑到这是一个“小清单”,几乎所有算法都将很快实现。IMO在可读性和简洁性上取胜,并且由于速度不是问题,因此使其完美。
Tim S.

2
这是莱夫效率比它可能是
Jodrell

74

这是另一种比Enumerable.Distinct+更有效的方法Enumerable.Count(如果序列不是集合类型,则更为有效)。它使用aHashSet<T>来消除重复项,在查找中非常有效并且具有计数属性:

var distinctBytes = new HashSet<byte>(theList);
bool allDifferent = distinctBytes.Count == theList.Count;

或另一种-更微妙和有效的方法:

var diffChecker = new HashSet<byte>();
bool allDifferent = theList.All(diffChecker.Add);

HashSet<T>.Add返回false如果因为它已经在该元素不能被添加HashSetEnumerable.All停在第一个“假”上。


1
如此简单和明显,我为什么不首先考虑它:)我在单元测试中使用了这种单行代码,以确认由我的出色代码生成的1000万个元素确实是唯一的Assert.IsTrue(samples.Add(AwesomeClass.GetUnique()));。他们曾经是,现在是:)为您+1蒂姆:)
grapkulec 2015年

1
我曾尝试你的答案对这个问题,但它是不工作的先生:stackoverflow.com/questions/34941162/...
学习型Overthinker混淆的

应该是这样的:bool allDifferent = theList.All(s => diffChecker.Add(s))
mike nelson

2
不,不需要。在这种情况下,您可以直接传递与会代表
Tim Schmelter

1
@AndréReichelt-我刚刚打开您的代码,List.All(HashSet.Add)在几乎所有情况下,第三个场景()似乎都比其他两个场景快得多
Kyle Delaney

6

好的,这是我可以想到的使用标准.Net的最有效方法

using System;
using System.Collections.Generic;

public static class Extension
{
    public static bool HasDuplicate<T>(
        this IEnumerable<T> source,
        out T firstDuplicate)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var checkBuffer = new HashSet<T>();
        foreach (var t in source)
        {
            if (checkBuffer.Add(t))
            {
                continue;
            }

            firstDuplicate = t;
            return true;
        }

        firstDuplicate = default(T);
        return false;
    }
}

本质上,如果您要做的就是找到第一个重复序列,那么将整个序列两次枚举的意义是什么。

我可以通过用特殊的空格和单个元素序列来进一步优化此效果,但这会以最小的增益降低可读性/可维护性。


很好地添加重复的值输出返回,对于验证非常有用
Pac0

我在这里测试了3种解决方案,这确实是此页面上最有效的。那里有一些错别字(例如sequence应该是source)。但是,一旦修复这些问题,效果就会很好
迈克·内尔森

@mikenelson,那应该更好
Jodrell

2
为了提高可读性,我认为应该if (!checkBuffer.Add(t)) { firstDuplicate = t; return true }在循环中。
tia

2

Distinct使用类似的逻辑GroupBy

var isUnique = theList.GroupBy(i => i).Count() == theList.Count;

如果您要检查某个属性的唯一性,theList.GroupBy(o => o.SomeProperty).Count() == theList.Count;而Distinct()不允许这样做,则这很有用。
1.0

1

一个还可以做:使用哈希集

var uniqueIds = new HashSet<long>(originalList.Select(item => item.Id));

            if (uniqueIds.Count != originalList.Count)
            {
            }

0

有很多解决方案。

毫无疑问,使用LINQ作为“ juergen d”和“ Tim Schmelter”使用的更漂亮。

但是,如果您没有“复杂性”和速度,那么最好的解决方案就是自行实现。解决方案之一是创建一个N大小的数组(字节为256)。然后循环数组,并且每次迭代都会测试匹配的数字索引(如果值是1的话),这意味着我已经增加了数组索引,因此数组没有区别,否则我将增加数组单元格并继续检查。


2
您可以使用256位= 32字节= 8个整数的位向量。但是您的Big O = O(n)仍然与使用其他答案中提出的Hashet相同。
BrokenGlass

这是O(n),所以可能最快(测试一下)。在您进行或结束时检查计数最快吗?我怀疑最终会改善最坏的情况,但是随着时间的推移,可能会改善平均水平和最佳情况。如果没有重复,这将是最坏情况下的性能。同样对于较大的数据类型,这将无法正常工作,对于16位类型,您将必须使用64k计数,以及64k位(8k字节),但是对于任何更大的数据,内存使用将开始变得愚蠢。但是我喜欢8bit值的答案。
ctrl-alt-delor

1
@TamusJRoyce,如果您想存储4294967296的可能性,则需要4GB而不是42MB(或其中512MB使用位掩码)
tigrou

不知道我在想什么。“分配42MB +的内存以容纳所有4294967296的可能性。并使用简单的存储桶计数器。甚至使用位屏蔽xor并检查是否将任何位从true更改为false。42MB+ / 8 = 5MB +对于当今的硬件,这笔开销似乎太大了。但是有一天,这可能值得。” 并不是真正的相关评论。哈希集是最好的。如果要处理非常大的阵列,则需要很大的内存。但是在这种奇怪的情况下,采用CRC算法的遗传算法会更好。将其映射到多项式。如果关闭,请评估。谢谢@tigrou!
TamusJRoyce

0

还有另一个解决方案,如果您要查找重复的值。

var values = new [] { 9, 7, 2, 6, 7, 3, 8, 2 };

var sorted = values.ToList();
sorted.Sort();
for (var index = 1; index < sorted.Count; index++)
{
    var previous = sorted[index - 1];
    var current = sorted[index];
    if (current == previous)
        Console.WriteLine(string.Format("duplicated value: {0}", current));
}

输出:

duplicated value: 2
duplicated value: 7

http://rextester.com/SIDG48202


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.