Frage C # effizientere Möglichkeit, zwei Sammlungen zu vergleichen


Ich habe zwei Sammlungen

List<Car> currentCars = GetCurrentCars();
List<Car> newCars = GetNewCars();

Ich möchte keine foreach-Schleife oder etwas verwenden, weil ich denke, dass es einen viel besseren Weg geben sollte, dies zu tun.

Ich suche nach einem effizienteren Weg, diese Sammlungen zu vergleichen und Ergebnisse zu erhalten:

  1. Liste der Autos in NewCars und nicht in aktuellenCars
  2. Liste der Autos, die nicht in NewCars und in CurrentCars sind

Typ Auto hat int Eigenschaft Id.

Es gab eine Antwort, die bereits gelöscht ist mit dem Hinweis Was ich meine, wenn ich effizient sage: weniger Code, weniger Mechanik und besser lesbare Fälle

So denken, was ist die Fälle, die ich habe?

Was wäre weniger Code, weniger Mechanik und besser lesbare Fälle?


7
2017-07-13 14:26


Ursprung


Antworten:


Sie können verwenden Except:

var currentCarsNotInNewCars = currentCars.Except(newCars);
var newCarsNotInCurrentCars = newCars.Except(currentCars);

Aber das hat keinen Leistungsvorteil gegenüber dem foreach Lösung. Es sieht einfach sauberer aus.
Beachten Sie auch die Tatsache, dass Sie implementieren müssen IEquatable<T> für dein Car Klasse, also wird der Vergleich auf der ID und nicht auf der Referenz durchgeführt.

Leistungsmäßig wäre ein besserer Ansatz, a nicht zu verwenden List<T> aber a Dictionary<TKey, TValue> mit der ID als Schlüssel:

var currentCarsDictionary = currentCars.ToDictionary(x => x.ID);
var newCarsDictionary = newCars.ToDictionary(x => x.ID);

var currentCarsNotInNewCars = 
    currentCarsDictionary.Where(x => !newCarsDictionary.ContainsKey(x.Key))
                         .Select(x => x.Value);

var newCarsNotInCurrentCars = 
    newCarsDictionary.Where(x => !currentCarsDictionary.ContainsKey(x.Key))
                     .Select(x => x.Value);

10
2017-07-13 14:28



Du kannst es so machen:

// 1) List of cars in newCars and not in currentCars
var newButNotCurrentCars = newCars.Except(currentCars);

// 2) List of cars in currentCars and not in newCars
var currentButNotNewCars = currentCars.Except(newCars);

Der Code verwendet die Aufzählbar. Außer Erweiterungsmethode (verfügbar in .Net 3.5 und höher).

Ich glaube, das erfüllt Ihre Kriterien "weniger Code, weniger Mechanik und besser lesbar".


12
2017-07-13 14:29



Wenn du mit ihnen anfängst HashSets können Sie das verwenden Except Methode.

HashSet<Car> currentCars = GetCurrentCars();
HashSet<Car> newCars = GetNewCars();

currentCars.Except(newCars);
newCars.Except(currentCars);

Es wäre viel schneller w / a als eine Liste. (Unter der Haube macht eine Liste nur eine foreach, Sätze können optimiert werden).


3
2017-07-13 14:31



Sie können LINQ verwenden ...

        List<Car> currentCars = new List<Car>();
        List<Car> newCars = new List<Car>();

        List<Car> currentButNotNew = currentCars.Where(c => !newCars.Contains(c)).ToList();
        List<Car> newButNotCurrent = newCars.Where(c => !currentCars.Contains(c)).ToList();

... aber lass dich nicht täuschen. Es mag weniger Code für Sie sein, aber es wird bestimmt einige for loops geben

EDIT: Habe nicht bemerkt, dass es eine Ausnahme-Methode gab :(


2
2017-07-13 14:31



Ich würde das übergehen Equals von einem Car um nach ID zu vergleichen und dann könnten Sie das verwenden IEnumerable.Except Erweiterungsmethode. Wenn Sie das nicht überschreiben können Equals Sie können Ihre eigenen erstellen IEqualityComparer<Car> die vergleicht zwei Autos nach ID.

class CarComparer : IEqualityComparer<Car>
{
    public bool Equals(Car x, Car y)
    {
        return x != null && y != null && x.Id == y.Id;
    }

    public int GetHashCode(Car obj)
    {
        return obj == null ? 0 : obj.Id;
    }
}

2
2017-07-13 14:29



Wenn Sie nach Effizienz suchen, implementieren Sie IComparable on Cars (Sortieren nach Ihrer eindeutigen ID) und verwenden Sie eine SortedList. Sie können dann Ihre Sammlungen gemeinsam durchgehen und Ihre Checks in O (n) auswerten. Dies führt natürlich zu zusätzlichen Kosten für List-Einsätze, um die sortierte Natur zu erhalten.


1
2017-07-13 14:32



Sie können die kleinere Liste in eine Hash-Tabellen-basierte Sammlung wie HashSet oder Dictionary kopieren und dann über die zweite Liste iterieren und prüfen, ob das Element in der Hash-Tabelle existiert.

das wird die Zeit von O (N ^ 2) in der naiven foreach innerhalb von Fall zu O (N) reduzieren.

Dies ist das Beste, was Sie tun können, ohne mehr über die Listen zu wissen (möglicherweise können Sie eine wenig besser, wenn die Listen zum Beispiel sortiert sind, aber, weil Sie jedes Auto mindestens einmal "berühren" müssen, um zu überprüfen, ob es auf der neuen Auto-Liste ist, können Sie nie besser als O (N) tun)


0
2017-07-13 14:34



Wenn ein Vergleich der Id - Eigenschaft ausreicht, um zu sagen, ob ein Auto gleich einem anderen ist, könnten Sie die Liste mit Ihrer eigenen Klasse überschreiben, die die Elemente verfolgt und die IEqualityComparerauf die gesamte Sammlung, so:

class CarComparer : IList<Car>, IEquatable<CarComparer>
{
    public bool Equals(CarComparer other)
    {
        return object.Equals(GetHashCode(),other.GetHashCode());
    }

    public override int GetHashCode()
    {
        return _runningHash;
    }

    public void Insert(int index, Car item)
    {
        // Update _runningHash here
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        // Update _runningHash here
        throw new NotImplementedException();
    }

    // More IList<Car> Overrides ....
}

Dann müssen Sie nur die Add, Removeusw. und andere Methoden, die sich auf die Elemente in der Liste auswirken können. Sie können dann eine private Variable beibehalten, bei der es sich um einen Hash der Art der IDs der Elemente in der Liste handelt. Beim Überschreiben Ihrer Equals Methoden können Sie dann nur diese private Variable vergleichen. Nicht der sauberste Ansatz bei weitem (wie Sie mit Ihrer Hash-Variable Schritt halten müssen), aber es wird dazu führen, dass Sie nicht durchlaufen müssen, um einen Vergleich zu machen. Wenn ich es wäre, würde ich nur Linq benutzen, wie einige hier erwähnt haben ...


0
2017-07-13 14:55