Ottimizza il codice C # per trovare oggetti non esistenti su List

1

Sto creando un modulo che produrrà un elenco di oggetti mancanti in un elenco. Attualmente il codice che ho impiegato è troppo lungo per un elenco di circa 300.000 oggetti.

public List<SimpleObject> GetNonExistentObjects()
{
     List<SimpleObject> source = GetNodes();//contains 300,000
     List<SimpleObject> target = GetNodes();//contains 300,005

     List<SimpleObject> nonExistentObjects = new List<SimpleObject>();

     foreach(Object obj in source)
     {
          bool existing = target.Any(x => x.Name == obj.Name && x.Label == obj.Label)
          if(!existing)
               nonExistentObjects.Add(obj);//Contains 5 objects
     }

     return nonExistentObjects;
}

Devo creare un sacco di metodi come questo, quindi vorrei chiedere come ottimizzare questo particolare metodo.

UPDATE: T è solo una semplice classe che ho creato che contiene 2 proprietà. Proprio quello

public class SimpleObject
{
     public string Name {get; set}
     public string Label {get; set;}
}
    
posta jmc 15.09.2016 - 16:41
fonte

4 risposte

8

Devi sostituire GetHashCode e Equals per la tua classe SimpleObject (e implementarli correttamente). Quindi puoi utilizzare il metodo Except di LINQ, come suggerito da @Oded in un commento :

nonExistentObjects = source.Except(target).ToList();

Except utilizza un HashSet<T> internamente , il che rende molto veloce.

Ecco un esempio su come implementare i due metodi che ho citato sopra:

public class SimpleObject
{
     public string Name { get; set; }
     public string Label { get; set; }

     public override bool Equals(object obj)
     {
         if (obj is SimpleObject)
         {
              var so = (SimpleObject)obj;
              return so.Name == Name && so.Label == Label;
         }
         return false;
     }

     public override int GetHashCode()
     {
         return Name.GetHashCode() ^ Label.GetHashCode();
     }
}
    
risposta data 15.09.2016 - 17:00
fonte
3

Questo mi sembra un uso non ottimale delle collezioni. Puoi usare HashSet invece di List per la raccolta di destinazione? Ricorda, in tal caso, dovrai ridefinire i metodi GetHashCode e Equals delle classi che verranno inserite in tali raccolte.

    
risposta data 15.09.2016 - 16:54
fonte
2

Inserisci i valori cercati in un IDictionary

quindi qualcosa di simile:

indexedTarget = target.ToDictionary(i=> i.Name + "_separator_" + i.Label)

indexedTarget.ContainsKey(obj.Uid  + "_separator_" + obj.Label);

link

come commenti del documento, la concatenazione delle stringhe per la chiave non è eccezionale. ci deve essere un modo per eseguire il test completo in una riga di codice senza.

indexedTarget = target.ToDictionary(i=> i.Name)

MyClass dupe;

if(IndexedTarget.TryGet(obj.Uid, out dupe) && dupe.Label != obj.Label) { nonExistentObjects.Add(dupe);}

modifica: nota sulle soluzioni di comparazione dell'uguaglianza.

Personalmente odio aggiungere i metodi GetHashCode e Equals aggiuntivi a classi poco numerose. È una buona soluzione quando si confrontano sempre le cose e c'è una definizione ferma di ciò che rende uguali due di loro. Ma può diventare oneroso quando la definizione non è chiara. cioè questa volta vuoi abbinare il nome AND all'etichetta, ma la prossima volta vuoi solo abbinare il nome

    
risposta data 15.09.2016 - 16:50
fonte
-1

Vorrei usare l'Overside e l'Equal di Doc Brown +1

public IEnumerable<SimpleObject> GetNonExistentObjects()
{
     List<SimpleObject>    source = GetNodes(); //contains 300,000
     // List.Add is O(1)
     HashSet<SimpleObject> target = GetNodes(); //contains 300,005 

     foreach(SimpleObject s in source)
     {
          if(!target.Contains(s))   // O(1)  LINQ ANY is O(N)
               yield s; //Contains 5 objects
     }
}
    
risposta data 15.09.2016 - 22:58
fonte

Leggi altre domande sui tag