2012-01-09 15 views
6

Ho due elenchi generici con 20.000 e 30.000 oggetti in ciascuna lista.Come confrontare due elenchi di grandi dimensioni in modo efficiente in C#?

class Employee 
{ 
    string name; 
    double salary; 
} 

List<Employee> newEmployeeList = List<Employee>() {....} // contains 20,000 objects 
List<Employee> oldEmployeeList = List<Employee>() {....} // contains 30,000 objects 

Le liste possono anche essere ordinate per nome se migliora la velocità.

voglio mettere a confronto queste due liste per scoprire

  1. dipendenti il ​​cui nome e stipendio corrispondenza
  2. dipendenti il ​​cui nome è corrispondenza, ma non di stipendio

Qual è il modo più veloce per confrontare elenchi di dati così grandi con condizioni sopra?

+1

È possibile utilizzare linq, ha un piccolo costo in termini di prestazioni, ma ancora una volta come @Jon ha detto è sufficiente per te o cos'altro hai provato? –

+1

Da dove prendi i tuoi dati? se stai compilando il tuo elenco da SQL, potresti volerlo confrontare direttamente da SQL e non dagli elenchi. –

+1

Dato che sono ordinati, un semplice traversamento sequenziale è O (n), è troppo lento? –

risposta

2

Vorrei ordinare entrambi gli elenchi newEmployeeList e oldEmployeeList per name - O(n*log(n)). E poi puoi usare l'algoritmo lineare per cercare le corrispondenze. Quindi il totale sarebbe O(n+n*log(n)) se entrambi gli elenchi hanno circa la stessa dimensione. Questo dovrebbe essere più veloce dell'algoritmo "forza bruta" O(n^2).

0

Uno dei più veloci possibili soluzioni su ordinato liste è l'uso di BinarySearch al fine di trovare un elemento in un altro elenco.

Ma, come mantioned altri, si dovrebbe misurare contro tuoi requisiti di progetto, come le prestazioni spesso tende ad essere una soggettiva cosa .

1

Si potrebbe creare un dizionario con

var lookupDictionary = list1.ToDictionary(x=>x.name); 

che darebbe si chiude a O (1) ricerca e una stretta a O (n) il comportamento, se stai cercando i valori da un ciclo sopra l'altro elenco.

(sto dando per scontato che ToDictionary è O (n) che avrebbe senso con un'implementazione dritto in avanti, ma non ho ancora testato questo per essere il caso)

Ciò farebbe per un dritto in avanti algoritmo, e sto pensando che andare sotto O (n) con due liste non ordinate è piuttosto difficile.

+1

Si è dimenticato di aggiungere la complessità dell'inizializzazione del dizionario – Elalfer

+0

Non si sa da dove arriverà il log (n), purché i bucket hash siano abbondanti, l'inserimento di un singolo elemento è praticamente un calcolo hash e un inserimento all'indice calcolato. –

+0

Sì, questo è il motivo per cui ho ** rimosso ** 'log (n)' dal mio commento – Elalfer

2

Probabilmente raccomanderei che le due liste vengano memorizzate in un Dictionary<string, Employee> in base al nome per iniziare, quindi è possibile scorrere le chiavi in ​​uno e cercare di vedere se esistono e gli stipendi corrispondono nell'altro. Ciò farebbe risparmiare anche il costo di ordinarli successivamente o di metterli in una struttura più efficiente.

Questo è praticamente O (n) - lineare per costruire entrambi i dizionari, lineare per passare attraverso le chiavi e cercare nell'altro. Dal momento che O (n + m + n) si riduce a O (n)

Ma, se è necessario utilizzare List<T> per contenere le liste per altri motivi, si potrebbe anche utilizzare il metodo Join() LINQ, e costruire una nuova lista con un campo Match che ti dice se erano una corrispondenza o mancata corrispondenza ...

 var results = newEmpList.Join(
      oldEmpList, 
      n => n.Name, 
      o => o.Name, 
      (n, o) => new 
       { 
        Name = n.Name, 
        Salary = n.Salary, 
        Match = o.Salary == n.Salary 
       }); 

È quindi possibile filtrare questo con una clausola Where() per Match o !Match.

2

Aggiornamento: Presumo (dal titolo della domanda) che i 2 elenchi siano già ordinati. Forse sono memorizzati in un database con un indice cluster o qualcosa del genere. Questa risposta, quindi, si basa su tale assunto.

Ecco un'implementazione con complessità O(n) ed è anche molto veloce, ed è anche piuttosto semplice.
Credo che questa sia una variante di Merge Algorithm.

Ecco l'idea:

  1. Inizio enumerazione entrambe le liste
  2. confrontare i 2 elementi attuali.
  3. Se corrispondono, aggiungi ai risultati.
    Se la prima voce è "più piccola", avanzare alla prima lista.
    Se il secondo elemento è "più piccolo", avanzare nella seconda lista.

Poiché entrambi gli elenchi sono noti per essere ordinati, questo funzionerà molto bene. Questa implementazione presuppone che name sia univoco in ciascuna lista.

var comparer = StringComparer.OrdinalIgnoreCase; 
var namesAndSalaries = new List<Tuple<Employee, Employee>>(); 
var namesOnly = new List<Tuple<Employee, Employee>>(); 

// Create 2 iterators; one for old, one for new: 
using (IEnumerator<Employee> A = oldEmployeeList.GetEnumerator()) { 
    using (IEnumerator<Employee> B = newEmployeeList.GetEnumerator()) { 
     // Start enumerating both: 
     if (A.MoveNext() && B.MoveNext()) { 
      while (true) { 
       int compared = comparer.Compare(A.Current.name, B.Current.name); 
       if (compared == 0) { 
        // Names match 
        if (A.Current.salary == B.Current.salary) { 
         namesAndSalaries.Add(Tuple.Create(A.Current, B.Current)); 
        } else { 
         namesOnly.Add(Tuple.Create(A.Current, B.Current)); 
        } 
        if (!A.MoveNext() || !B.MoveNext()) break; 
       } else if (compared == -1) { 
        // Keep searching A 
        if (!A.MoveNext()) break; 
       } else { 
        // Keep searching B 
        if (!B.MoveNext()) break; 
       } 

      } 
     } 
    } 
} 
+0

Non dovrebbero essere entrambi gli elenchi ordinati prima di utilizzare l'algoritmo? In questo caso non puoi rivendicare la complessità di 'O (n)'. È almeno 'O (n * ln (n) + n)' per eq. elenchi di misure – Elalfer

+0

"Come confrontare due elenchi di grandi dimensioni in modo efficiente in C#?" Stavo correndo supponendo che le liste fossero, in effetti, ordinate. Tuttavia, il suo commento "Gli elenchi possono anche essere ordinati per nome se migliora la velocità" potrebbe indicare che gli elenchi non sono ordinati, o potrebbe indicare che l'origine degli elenchi può essere preordinata (ad esempio, un indice cluster) . Quindi immagino ci sia un po 'di ambiguità nella domanda. Aggiornerò la mia risposta con un disclaimer –

Problemi correlati