2008-10-02 10 views
10

Imaginez le type:meilleur algorithme pour synchroniser deux IList en C# 2.0

public struct Account 
{ 
    public int Id; 
    public double Amount; 
} 

Quel est le meilleur algorithme pour synchroniser deux IList<Account> dans la version 2.0 C#? (Pas de linq)?

La première liste (L1) est la liste de référence, la seconde (L2) est l'une de synchroniser selon le premier:

  • Tous les comptes à L2 qui ne sont plus présents dans L1 doivent être supprimés de L2
  • Tous les comptes en L2 qui existent encore en L1 doit être mis à jour (attribut quantité)
  • Tous les comptes qui sont en L1 mais pas encore en L2 doivent être ajoutés à L2

Le Id identifie acc ounts. Il n'est pas trop difficile de trouver un algorithme naïf et fonctionnel, mais j'aimerais savoir s'il existe une solution intelligente pour gérer ce scénario sans ruiner la lisibilité et les perfs.

EDIT:

  • Type de compte n'a pas d'importance, est peut être une classe, a des propriétés, des membres de l'égalité, etc.
  • L1 et L2 ne sont pas triés
  • articles L2 pourrait ne doit pas être remplacé par les éléments L1, ils doivent être mis à jour (champ par champ, propriété par propriété)
+0

Y at-il une bonne raison pour laquelle vous ne pouvez pas simplement copier la liste de référence? L2 = L1; ressemble à ce dont vous avez besoin en fonction des critères que vous avez donnés. –

+0

Oui, la liste L2 est une BindingList utilisée comme source de données pour une grille. Les objets L1 et L2 ne sont pas du même type. L2 contient des objets de présentation qui doivent être mis à jour en ce qui concerne les performances et le comportement de clignotement de la grille. –

Répondre

5

Pour commencer, je voudrais me débarrasser de la structure mutable. Les types de valeur mutables sont une chose fondamentalement mauvaise. (Comme le sont les champs publics, IMO.)

Il est probablement utile de construire un dictionnaire afin de pouvoir facilement comparer le contenu des deux listes. Une fois que vous avez une façon simple de vérifier la présence/absence, le reste devrait être simple.

Pour être honnête cependant, il semble que vous voulez essentiellement que L2 soit une copie complète de L1 ... effacer L2 et juste appeler AddRange? Ou est-ce que vous voulez aussi prendre d'autres mesures pendant que vous changez de L2?

+0

Je suis d'accord sur les structures mutables et les champs publics. Mais le type de compte n'est pas vraiment important ici - il peut s'agir d'une classe, de membres d'encapsulation et d'égalité, etc. Je suis concentré sur la synchronisation de deux listes en prenant soin de ne pas remplacer les éléments L2 par les éléments L1. Les éléments L2 doivent être mis à jour. –

0

En plus du commentaire de Jon Skeet, créez une classe pour votre compte et remplacez la méthode Equals() et GetHashCode() pour obtenir une bonne vérification de l'égalité.

0

L2 = L1.clone()?

... mais je suppose que vous avez oublié de mentionner quelque chose.

2

Si vos deux listes sont triées, vous pouvez simplement les parcourir en tandem. C'est une opération O (m + n). Le code suivant pourrait aider:

class Program 
{ 
    static void Main() 
    { 
     List<string> left = new List<string> { "Alice", "Charles", "Derek" }; 
     List<string> right = new List<string> { "Bob", "Charles", "Ernie" }; 

     EnumerableExtensions.CompareSortedCollections(left, right, StringComparer.CurrentCultureIgnoreCase, 
      s => Console.WriteLine("Left: " + s), s => Console.WriteLine("Right: " + s), (x,y) => Console.WriteLine("Both: " + x + y)); 
    } 
} 

static class EnumerableExtensions 
{ 
    public static void CompareSortedCollections<T>(IEnumerable<T> source, IEnumerable<T> destination, IComparer<T> comparer, Action<T> onLeftOnly, Action<T> onRightOnly, Action<T, T> onBoth) 
    { 
     EnumerableIterator<T> sourceIterator = new EnumerableIterator<T>(source); 
     EnumerableIterator<T> destinationIterator = new EnumerableIterator<T>(destination); 

     while (sourceIterator.HasCurrent && destinationIterator.HasCurrent) 
     { 
      // While LHS < RHS, the items in LHS aren't in RHS 
      while (sourceIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) < 0)) 
      { 
       onLeftOnly(sourceIterator.Current); 
       sourceIterator.MoveNext(); 
      } 

      // While RHS < LHS, the items in RHS aren't in LHS 
      while (sourceIterator.HasCurrent && destinationIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) > 0)) 
      { 
       onRightOnly(destinationIterator.Current); 
       destinationIterator.MoveNext(); 
      } 

      // While LHS==RHS, the items are in both 
      while (sourceIterator.HasCurrent && destinationIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) == 0)) 
      { 
       onBoth(sourceIterator.Current, destinationIterator.Current); 
       sourceIterator.MoveNext(); 
       destinationIterator.MoveNext(); 
      } 
     } 

     // Mop up. 
     while (sourceIterator.HasCurrent) 
     { 
      onLeftOnly(sourceIterator.Current); 
      sourceIterator.MoveNext(); 
     } 

     while (destinationIterator.HasCurrent) 
     { 
      onRightOnly(destinationIterator.Current); 
      destinationIterator.MoveNext(); 
     } 
    } 
} 

internal class EnumerableIterator<T> 
{ 
    private readonly IEnumerator<T> _enumerator; 

    public EnumerableIterator(IEnumerable<T> enumerable) 
    { 
     _enumerator = enumerable.GetEnumerator(); 
     MoveNext(); 
    } 

    public bool HasCurrent { get; private set; } 

    public T Current 
    { 
     get { return _enumerator.Current; } 
    } 

    public void MoveNext() 
    { 
     HasCurrent = _enumerator.MoveNext(); 
    } 
} 

Vous devrez faire attention à la modification des collections tout en réitérant sur eux, cependant.

Si elles ne sont pas triées, alors comparer chaque élément de l'un avec chaque élément de l'autre est O (mn), ce qui devient douloureux très rapidement.

Si vous pouvez supporter de copier les valeurs de clé de chaque collection dans un dictionnaire ou similaire (c.-à-d.une collection avec une performance acceptable lorsqu'on lui demande «est X présent?»), alors vous pourriez trouver quelque chose de raisonnable.

+0

Eh bien, vous devez trouver quelque chose de mieux que O (n * log (n) + m * log (m)), sinon je vais juste trier les listes et utiliser votre première approche :) – Tetha

0

Je sais que c'est un ancien article, mais vous devriez vérifier AutoMapper. Il fera exactement ce que vous voulez d'une manière très flexible et configurable.

AutoMapper

Questions connexes