リスト内のすべての値が一意であるかどうかをテストします


90

バイトの小さなリストがあり、それらがすべて異なる値であることをテストしたいと思います。たとえば、私はこれを持っています:

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

すべての値が異なるかどうかを確認するための最良の方法は何ですか?


2
これは典型的な教室の質問なので、質問で答えます。並べ替えたらどうしますか?
ctrl-alt-delor 2013

回答:


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

ただ好奇心が強い:これのスペースと時間の要件は何ですか?
dtb 2013

10
@dtbは約O(N)である必要があります。もちろん、これが「小さなリスト」であることを考えると、ほとんどすべてのアルゴリズムで超高速になります。IMOは読みやすさと簡潔さで勝ちます。速度は問題ではないので、完璧です。
ティムS.

2
これは、それができるよりも効率的です
Jodrell

74

Enumerable.Distinct+よりも効率的な別のアプローチがありますEnumerable.Count(シーケンスがコレクションタイプでない場合はなおさらです)。これは、使用してHashSet<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>.Addfalse要素がすでにHashSet。にあるために追加できなかった場合に戻ります。 Enumerable.All最初の「false」で停止します。


1
とてもシンプルで明白なのに、なぜ最初に考えなかったのですか:)ユニットテストでこのワンライナーを使用して、素晴らしいコードによって生成された1,000万個の要素が本当にユニークであることを確認しましたAssert.IsTrue(samples.Add(AwesomeClass.GetUnique()));。彼らは今もそうです:)あなたのために+1ティム:)
grapkulec 2015

1
私はこの質問にあなたの答えを試してみましたが、それは先生が動作していない:stackoverflow.com/questions/34941162/...
学習-Overthinker-混乱

これである必要があります:bool allDifferent = theList.All(s => diffChecker.Add(s))
マイクネルソン2017年

2
いいえ、必要ありません。この場合、代理人を直接渡すことができます
Tim Schmelter 2017年

1
@AndréReichelt-コードを開いたところですが、3番目のシナリオ(List.All(HashSet.Add))は、ほとんどすべての場合、他の2つよりもはるかに高速であるようです
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;
    }
}

基本的に、最初の重複を見つけるだけの場合、シーケンス全体を2回列挙することのポイントは何ですか。

空の単一要素シーケンスを特別にケーシングすることでこれをさらに最適化できますが、最小限のゲインで読みやすさ/保守性が低下します。


重複した値を追加して返すと、検証に非常に役立ちます
Pac0 2017

私はここで3つのソリューションをテストしましたが、これは確かにこのページで最も効率的です。ただし、そこにはいくつかのタイプミスがあります(たとえば、あるsequenceはずですsource)。しかし、これらが修正されるとうまく機能します
mike nelson 2017年

@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()で許可されていないときに、プロパティに関する一意性を確認する場合に役立ちます。
Rev1。

1

次のこともできます:ハッシュセットを使用する

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

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

0

多くの解決策があります。

そして、間違いなく、「juergend」や「TimSchmelter」としてLINQを使用したより美しいものが言及されています。

しかし、「複雑さ」とスピードをむき出しにする場合、最善の解決策はそれを自分で実装することです。解決策の1つは、Nサイズの配列を作成することです(バイトの場合は256です)。そして、配列をループします。すべての反復で、値が1の場合、一致する番号インデックスをテストします。つまり、すでに配列インデックスをインクリメントしているため、配列が区別されません。それ以外の場合は、配列セルをインクリメントしてチェックを続行します。 。


2
256ビット= 32バイト= 8整数のビットベクトルを使用できます。ただし、Big O = O(n)は、他の回答で提案されているHashetを使用した場合と同じです。
BrokenGlass 2013

これはO(n)なので、おそらく最速です(テストしてください)。あなたが行くにつれて、または最後にカウントをチェックするのが最も速いでしょうか?最終的には最悪の場合が改善されると思いますが、進むにつれて平均と最良の場合が改善される可能性があります)。重複がない場合、これは最悪の場合のパフォーマンスになります。また、より大きなデータ型の場合、これはうまく機能しません。16ビット型の場合、64kのカウント、64kビット(8kバイト)を使用する必要がありますが、それより大きいデータ型では、メモリ使用量がばかげ始めます。しかし、私は8ビット値のこの答えが好きです。
ctrl-alt-delor 2013

1
@TamusJRoyceあなたは4294967296の可能性を保存したい場合は、あなたが42メガバイト(またはビットマスクを使用して512MBの)4ギガバイトを必要はありません
tigrou

私が何を考えていたのかわからない。「4294967296のすべての可能性を保持するために42MB +のメモリを割り当てます。単純なバケットカウンタを使用します。または、ビットマスキング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


0

IEnumerable(aray、listなど)が次のように一意であるかどうかを確認します。

var isUnique = someObjectsEnum.GroupBy(o => o.SomeProperty).Max(g => g.Count()) == 1;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.