2011-05-13 2 views

Odpowiedz

13

Należy to zrobić:

list1.Union(list2).Distinct(aCustomComparer).ToList() 
+0

Czy to nie zbędne do korzystania Wyraźny() po Unii()? – 0xbadf00d

+2

Zależy od warunków związku. Domyślnie metoda Union() będzie deduplikować tylko te obiekty, które są takie same. Ponieważ Distinct używa niestandardowego IEqualityComparer, to faktycznie zrobi coś więcej niż związek.ALE, że IEqualityComparer może również zostać przekazany Unii bezpośrednio do wykorzystania jako jej dedupert, więc jest nieco zbędny – KeithS

+0

"Union" używa domyślnego porównywania równości, który, w zależności od typu, może, ale nie musi, być porównawczym porównaniem. Zobacz https://msdn.microsoft.com/en-us/library/vstudio/bb341731(v=vs.100).aspx i https://msdn.microsoft.com/en-us/library/vstudio/bb341731 (v = vs.100) .aspx. Ponadto, jak już wspomniałeś, lepiej jest dostarczyć niestandardowego porównywalnika do metody "Union" i wyeliminować wyraźne wezwanie do czytelności i (być może?) Niewielki wzrost wydajności. –

6

Dopóki oni IEnumerable, można użyć go-to Linq odpowiedź:

var union = firstCollection.Union(secondCollection); 

ten użyje domyślnego porównania równości, które dla większości obiektów jest równością referencyjną. Aby to zmienić, możesz zdefiniować rodzajowy IEqualityComparer na typ elementu w swojej kolekcji, który dokona bardziej semantycznego porównania i określić go jako drugi argument Unii.

2

Innym sposobem, aby dodać do swojej listy objąć istniejącymi byłoby:

list1.AddRange(list2.Distinct().Except(list1)); 
+0

Jest to w zasadzie operacja za Union(), BTW. funkcja Except() będzie wywoływać Any (x => x == Current) na podanej liście1 i zwracać elementy z listy2, gdzie jest to fałsz. – KeithS

+0

Oprócz unii nie modyfikuje oryginalnej listy, OP żądający "dodania odrębnych elementów drugiego ICollection do istniejącego", Union zwraca nową kolekcję. –

+0

Moja odpowiedź może nie być tym, co OP chciał, ale jego pytanie jest wystarczająco niejednoznaczne, aby mogło być. –

2

Najbardziej bezpośrednią odpowiedź na Twoje pytanie - skoro nie daje wiele szczegółów na rzeczywistych typów kolekcji ICollection masz jako wejście lub potrzeby jako wyjście jest ta podana przez KeithS

var union = firstCollection.Union(secondCollection); 

ten powróci odrębną IEnumerable - jeśli to jest to, czego potrzebujesz to jest bardzo szybki. Zrobiłem małą aplikację testową (poniżej), która uruchomiła metodę unii (MethodA) przeciwko prostej metodzie deduplikacji hashset i zwraca zestaw Hashset <> (Metoda B). Metoda unia niszczy hashset:

sposób A: 1ms

MethodB: 2827ms

Jednak - uwzględniając, że IEnumerable do konwersji do innego typu kolekcji, takich jak listy <> (jak ADAS wersja wysłane) zmienia wszystko:

prostu dodając .ToList(), aby sposób A

var union = firstCollection.Union(secondCollection).ToList(); 

Zmienia wyniki:

sposób A: 3656ms

MethodB: 2803ms

Tak - wydaje się bardziej trzeba będzie wiadomo o tym konkretnym przypadku, na którym pracujemy - oraz wszelkie rozwiązanie, które wymyślisz, powinno zostać przetestowane - ponieważ niewielka (kod) zmiana może mieć OGROMNE skutki.

Poniżej jest test użyłem porównania tych metod - jestem pewien, że to głupi sposób, aby przetestować - ale wydaje się działać :)

private static void Main(string[] args) 
    { 
     ICollection<string> collectionA = new List<string>(); 
     ICollection<string> collectionB = new List<string>(); 
     for (int i = 0; i < 1000; i++) 
     { 
      string randomString = Path.GetRandomFileName(); 
      collectionA.Add(randomString); 
      collectionA.Add(randomString); 
      collectionB.Add(randomString); 
      collectionB.Add(randomString); 
     } 
     Stopwatch testA = new Stopwatch(); 
     testA.Start(); 
     MethodA(collectionA, collectionB); 
     testA.Stop(); 


     Stopwatch testB = new Stopwatch(); 
     testB.Start(); 
     MethodB(collectionA, collectionB); 
     testB.Stop(); 

     Console.WriteLine("MethodA: {0}ms", testA.ElapsedMilliseconds); 
     Console.WriteLine("MethodB: {0}ms", testB.ElapsedMilliseconds); 
     Console.ReadLine(); 
    } 

    private static void MethodA(ICollection<string> collectionA, ICollection<string> collectionB) 
    { 
     for (int i = 0; i < 10000; i++) 
     { 
      var result = collectionA.Union(collectionB); 
     } 
    } 

    private static void MethodB(ICollection<string> collectionA, ICollection<string> collectionB) 
    { 
     for (int i = 0; i < 10000; i++) 
     { 
      var result = new HashSet<string>(collectionA); 
      foreach (string s in collectionB) 
      { 
       result.Add(s); 
      } 
     } 
    } 
+0

Po to, aby być jasne; ZAWSZE jest bardziej wydajnym rozwiązaniem niż Linq. Jednak w przypadku 99,9% z tego, co programowałeś w .NET, na początku (w przeciwieństwie do bardziej ojczystego języka, który wysadzi dowolną implementację .NET z wody), Linq ma dobrą wydajność i zazwyczaj bardziej czytelną i zrozumiałą implementację . – KeithS

+0

Powodem, dla którego MethodA jest o wiele szybszy, jest to, że nie wylicza elementów kolekcji, którą zwraca, dlatego wywoływanie ToList powoduje, że jest ona wolniejsza. –