7
votes

C # moyen plus efficace de comparer deux collections

J'ai deux collections xxx

Je ne veux pas utiliser de boucle ou quelque chose parce que je pense qu'il devrait y avoir beaucoup de meilleure façon de le faire.

i Je cherche un moyen plus efficace de comparer ces collections et d'obtenir des résultats:

  1. Liste des voitures qui se trouvent à Newcars ​​et non dans CurrentCars
  2. Liste des voitures qui ne sont pas dans Newcars ​​et en CurrentCars

    type de type AT ID de propriété.

    Il y avait une réponse, qui est déjà supprimée en disant Ce que je veux dire en disant efficacement: moins de code, moins de mécanique et des cas plus lisibles

    Alors, pensant ainsi quel est les cas que j'ai?

    Quel serait moins de code, moins de mécanique et des cas plus lisibles?


2 commentaires

Les opérations que vous décrivez sont des "différences définies". Si vous devez calculer beaucoup de différences de réglage, ou sur des ensembles volumineux, vous ne devez pas utiliser la liste en premier lieu. Vous devez utiliser HASHSET , une structure de données spécifiquement optimisée pour calculer ces types de différences.


@ Sciences lippert dans mon cas, ce sont de très petits ensembles (1-3 articles), donc je pense qu'il n'y a pas besoin de hashset .


8 Réponses :


11
votes

Vous pouvez utiliser sauf : xxx

mais cela n'a aucun avantage de performance sur la solution foreach . Ça a l'air plus propre.
Aussi, soyez conscient du fait que vous devez implémenter iéquatif pour votre classe voiture , la comparaison est donc effectuée sur l'ID et non sur la référence.

performant dans le sens de la performance, une meilleure approche serait de ne pas utiliser de liste list mais un dictionnaire avec l'identifiant comme clé : xxx


5 commentaires

Mais c'est en essence une boucle


@LANDONCZ: Je sais. Je pense qu'il est impossible de ne pas utiliser de boucle.


Je suis d'accord, techniquement, vous devez utiliser une boucle à moins que vous ne fassiez à une récursion folle (alors vous allez avoir quelque chose d'inacceptable et risque de souffler la pile).


C'est sûr que j'allais dire cela aussi, mais décidé contre cela pour la brièveté.


@Daniel Hilgarth Première chose que j'ai essayé la méthode .Except (), mais que j'ai réalisé que je dois mettre en œuvre Iequatable comme vous l'avez dit, car j'ai besoin de comparaison que par ID. L'approche du dictionnaire fonctionnant bien pour moi, donc je n'ai pas besoin d'implémenter des interfaces, je le ferais, mais j'utilise des classes générées automatiques (POCO, donc la voiture est autogogenerated Classe de POCO) donc je ne veux donc pas modifier le modèle T4 ou Créez une classe partielle afin de mettre en œuvre IEQUABLE .



14
votes

Vous pouvez le faire comme ceci:

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


3 commentaires

+1, Linq est probablement un moyen simple, en particulier avec Sauf () .


+1 Mais il n'y a aucune garantie que, sauf exercer une exécution A pour chacun puisqu'il utilisera probablement iEnumerable et pour acheter de toutes les manières.


Il a dit qu'il ne voulait pas une boucle de Foreach, cela pourrait être à cause de la lisibilité, il n'a pas précisé. Je vais aller à côté de la question collante des performances et donne le code le plus lisible et le plus maintenu.



2
votes

Je voudrais remplacer le égal code> d'un voiture code> pour comparer par ID, puis vous pouvez utiliser la méthode ienumerable.except code> méthode d'extension. Si vous ne pouvez pas remplacer le égale code> Vous pouvez créer votre propre iéqualitycomparer code> qui compare deux voitures par 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;
    }
}


3 commentaires

Je pense que c'est la seule façon de le faire sans boucle à travers chaque article de la manière.


@LANDONCZ: Je ne vois pas comment cela évite une boucle. Il suggère également d'utiliser le à l'exception de la méthode d'extension , bien qu'il l'a mal orthographié.


Désolé, je pensais qu'il remplace toute la liste comme ça, d'oh, devinez que vous ne pouvez pas ajouter de code aux réponses ...



2
votes

Vous pouvez utiliser LINQ ...

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


0 commentaires

3
votes

Si vous commencez avec eux dans Hashset code> s Vous pouvez utiliser le sauf code> méthode.

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

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


3 commentaires

+1 pour hashset. Bien qu'il fonctionnera plus rapidement, cela consommera plus de mémoire et le coût initial de remplissage du hashset sera plus élevé. Vous ne devez utiliser que HASHSET si vous envisagez d'utiliser la même collection plusieurs fois.


Très vrai. Le compromis est presque toujours la taille de la vitesse, cependant.


Hashset a-t-il une spéciale, sauf que ce n'est pas la méthode d'extension LINQ? Je pense que vous vouliez dire sauf, qui mutait en place.



1
votes

Si vous recherchez une efficacité, implémentez Icomarable sur les voitures (triant sur votre identifiant unique) et utilisez une tige de tri. Vous pouvez ensuite parcourir vos collections ensemble et évaluer vos chèques dans O (N). Ceci est bien sûr fourni avec un coût supplémentaire pour la liste des inserts pour maintenir la nature triée.


0 commentaires

1
votes

Vous pouvez copier la liste plus petite dans une collection basée sur une table de hachage, comme HASHSET ou Dictionnaire, puis itérer sur la deuxième liste et vérifier si l'élément existe dans la table de hachage.

Cela réduira le temps de O (n ^ 2) dans la naïve pourche à l'intérieur de l'affaire O (n).

C'est le meilleur que vous puissiez faire sans en savoir plus sur les listes (vous pourrez peut-être faire un petit mieux si les listes sont triées par exemple, mais, puisque vous devez "toucher" Chaque voiture au moins une fois pour vérifier si c'est sur la nouvelle liste de voitures, vous ne pouvez jamais faire mieux que O (n))


0 commentaires

0
votes

Si une comparaison de la propriété ID vous suffira de dire si une voiture est égale à une autre, afin d'éviter une sorte de boucle, vous pouvez remplacer la liste avec votre propre classe qui garde la trace des articles et utilise le IéqualityComparer Code> sur toute la collection, comme celui-ci:

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


0 commentaires