Alle bisher genannten Lösungen führen bisher ein GroupBy durch. Auch wenn ich nur den ersten Duplikat benötige, werden alle Elemente der Sammlungen mindestens einmal aufgelistet.
Die folgende Erweiterungsfunktion hört auf zu zählen, sobald ein Duplikat gefunden wurde. Es wird fortgesetzt, wenn ein nächstes Duplikat angefordert wird.
Wie immer gibt es in LINQ zwei Versionen, eine mit IEqualityComparer und eine ohne.
public static IEnumerable ExtractDuplicates(this IEnumerable source)
{
return source.ExtractDuplicates(null);
}
public static IEnumerable ExtractDuplicates(this IEnumerable comparer);
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (comparer == null)
comparer = EqualityCompare.Default;
HashSet foundElements = new HashSet(comparer);
foreach (TSource sourceItem in source)
{
if (!foundElements.Contains(sourceItem))
{ // wir haben dieses sourceItem noch nicht gesehen. Fügen Sie es zu den gefundenen Elementen hinzu
foundElements.Add(sourceItem);
}
else
{ // wir haben dieses Element schon mal gesehen. Es ist ein Duplikat!
yield return sourceItem;
}
}
}
Verwendung:
IEnumerable myObjects = ...
// überprüfen, ob Duplikate vorhanden sind:
bool hasDuplicates = myObjects.ExtractDuplicates().Any();
// oder die ersten drei Duplikate finden:
IEnumerable first3Duplicates = myObjects.ExtractDuplicates().Take(3)
// oder die ersten 5 Duplikate finden, die einen Namen = "MeinName" haben
IEnumerable myNameDuplicates = myObjects.ExtractDuplicates()
.Where(duplicate => duplicate.Name == "MeinName")
.Take(5);
Für all diese LINQ-Anweisungen wird die Sammlung nur geparst, bis die angeforderten Elemente gefunden sind. Der Rest der Sequenz wird nicht interpretiert.
Meiner Meinung nach ist das eine Effizienzsteigerung, die es zu berücksichtigen gilt.