Listedeki tüm değerlerin benzersiz olup olmadığını test edin


91

Küçük bir bayt listem var ve hepsinin farklı değerler olup olmadığını test etmek istiyorum. Örneğin, şuna sahibim:

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

Tüm değerlerin farklı olup olmadığını kontrol etmenin en iyi yolu nedir?


2
Bu tipik bir sınıf sorusu olduğu için bir soru ile cevaplayacağım. Sıralanmış olsaydı nasıl yapardın?
ctrl-alt-delor

Yanıtlar:


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

Merak ediyorum: bunun uzay ve zaman gereksinimleri nelerdir?
dtb

10
@dtb , O (N) ile ilgili olmalıdır . Elbette, bunun "küçük bir liste" olduğu düşünülürse, hemen hemen her algoritma ile yıldırım hızında olacaktır. IMO bu, okunabilirlik ve özlülük konusunda kazanır ve hız bir sorun olmadığı için onu mükemmel kılar.
Tim S.

2
Bu, olabileceğinden çok daha verimli
Jodrell

74

İşte Enumerable.Distinct+ ' dan daha verimli olan başka bir yaklaşım Enumerable.Count(dizi bir koleksiyon türü değilse de daha fazlası). Bu bir kullanır HashSet<T>çiftleri ortadan kaldıran aramalarını çok verimli ve bir sayım-özelliği vardır:

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

veya başka - daha incelikli ve verimli - yaklaşım:

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

HashSet<T>.Addfalseöğe zaten içinde olduğu için eklenemezse döndürür HashSet. Enumerable.Allilk "yanlış" üzerinde durur.


1
çok basit ve açık, neden ilk önce bunu düşünmedim :) Bu tek satırlık kalemi, harika kodum tarafından oluşturulan 10 milyon öğenin gerçekten benzersiz olduğunu doğrulamak için birim testinde kullandım Assert.IsTrue(samples.Add(AwesomeClass.GetUnique()));. Onlar ve onlar :) Senin için +1 Tim :)
grapkulec

1
cevabınızı bu soruya denedim ama çalışmıyor efendim: stackoverflow.com/questions/34941162/…
Learning-Overthinker-Confused

Bu olmalı:bool allDifferent = theList.All(s => diffChecker.Add(s))
mike nelson

2
Hayır, gerekli değil. Bu durumda delegeyi doğrudan geçebilirsiniz
Tim Schmelter

1
@ AndréReichelt - Kodunuzu yeni açtım ve üçüncü senaryo ( List.All(HashSet.Add)) neredeyse her durumda diğer ikisinden çok daha hızlı görünüyor
Kyle Delaney

7

Tamam, işte standart .Net kullanmayı düşünebildiğim en verimli yöntem.

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;
    }
}

esasen, tek yapmak istediğiniz ilk kopyayı bulmaksa, tüm diziyi iki kez numaralandırmanın amacı nedir?

Bunu, boş ve tek bir öğe dizilerini özel olarak muhafaza ederek daha fazla optimize edebilirdim, ancak bu, minimum kazançla okunabilirlik / sürdürülebilirlikten düşecektir.


Güzel, dönüş dışarı doğrulama için oldukça yararlı bir yinelenen değer ekleyerek
Pac0

Burada 3 çözümü test ettim ve bu gerçekten bu sayfadaki en verimli çözüm. Yine de birkaç yazım hatası (örneğin sequenceolmalı source). Ancak bunlar düzeltildikten sonra harika çalışıyor
mike nelson

@mikenelson, bu daha iyi olmalı
Jodrell

2
Okunabilirlik için, if (!checkBuffer.Add(t)) { firstDuplicate = t; return true }döngü içinde olması gerektiğini düşünüyorum .
tia

2

DistinctKullanmaya benzer mantık GroupBy:

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

theList.GroupBy(o => o.SomeProperty).Count() == theList.Count;Distinct () buna izin vermezken , bir özelliğe göre benzersizliği kontrol etmek istiyorsanız bu yararlıdır .
Rev1.0

1

Ayrıca şunları da yapabilirsiniz: Hashset kullanma

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

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

0

Pek çok çözüm var.

Ve şüphesiz LINQ'nun "juergen d" ve "Tim Schmelter" olarak kullanılmasıyla daha güzel olanlar.

Ancak, "Karmaşıklığı" ve hızı açıklarsanız, en iyi çözüm bunu kendi başınıza uygulamak olacaktır. Çözümlerden biri, N boyutunda bir dizi oluşturmak olacaktır (bayt için 256'dır). Ve diziyi döngüye sokun ve her yinelemede, değer 1 ise eşleşen sayı indeksini test edecek, yani dizi indeksini zaten artırıyorum ve bu nedenle dizi farklı değil, aksi takdirde dizi hücresini artıracağım ve kontrol etmeye devam edeceğim .


2
256 bit = 32 bayt = 8 tamsayı olan bir bit vektörü kullanabilirsiniz. Ancak Büyük O = O (n) değeriniz, diğer cevapta önerilen bir Hashet kullanmakla aynı olacaktır.
BrokenGlass

Bu O (n) bu yüzden belki en hızlı, (test edin). Siz ilerledikçe veya sonunda kontrol etmek en hızlısı mı olur? Sonunda en kötü durumu iyileştireceğinden şüpheleniyorum, ancak ilerledikçe ortalama ve en iyi durumu iyileştirebilir). Yineleme yoksa, bu en kötü durum performansı olacaktır. Ayrıca daha büyük veri türleri için bu iyi çalışmayacaktır, 16 bitlik bir tip için 64k sayım, yani 64k bit (8k bayt) kullanmanız gerekir, ancak daha büyük herhangi bir şey için bellek kullanımı saçma olmaya başlayacaktır. Ancak bu cevabı 8bit değerler için seviyorum.
ctrl-alt-delor

1
@TamusJRoyce 4294967296 olasılıklarını saklamak istiyorsanız, 42MB'ye değil 4GB'a ihtiyacınız var (veya 512MB'ınız bit maskeleme kullanıyor)
tigrou 01

Ne düşündüğümden emin değilim. "Tüm 4294967296 olasılıklarını saklamak için 42 MB + bellek ayırın. Ve basit kova sayaçları kullanın. Veya bit maskeleme xor kullanın ve herhangi bir bitin doğrudan yanlışa değiştirilip değiştirilmediğini kontrol edin. 42MB + / 8 = 5MB + Gider bugünün donanımıyla çok büyük görünüyor. Ancak bir gün bu haklı olabilir. " gerçekten alakalı bir yorum değil. Hashset en iyisidir. Son derece büyük dizilerle uğraşıyorsanız, son derece büyük bir bellek parçası beklersiniz. Ancak böylesine garip bir durumda, CRC algoritmasına sahip bir sapkınlık daha iyi olurdu. Bir polinomla eşleştirin. Yakınsa, değerlendirin. Teşekkür ederim @tigrou!
TamusJRoyce

0

Yinelenen değerleri bulmak istiyorsanız başka bir çözüm.

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));
}

Çıktı:

duplicated value: 2
duplicated value: 7

http://rextester.com/SIDG48202


0

IEnumerable'ın (aray, list, vb.) Aşağıdaki gibi benzersiz olup olmadığını kontrol ederim:

var isUnique = someObjectsEnum.GroupBy(o => o.SomeProperty).Max(g => g.Count()) == 1;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.