2010-10-09 14 views
8

mam HashSet,Odejmij HashSets (i zwróć kopię)?

var universe = new HashSet<int>(); 

i kilka podgrup,

var sets = new List<HashSet<int>>(numSets); 

Chcę odjąć kawałek, który można zrobić tak:

var remaining = universe.ExceptWith(sets[0]); 

Ale ExceptWith działa w miejscu. Nie chcę modyfikować universe. Czy najpierw powinienem to sklonować, czy jest lepszy sposób?

+0

To znaczy, chcesz wiedzieć, jak sklonować zestaw hash? – kennytm

+0

@KennyTM: Chodzi mi o to, że chcę wiedzieć, jak wykonać zadanie. Jeśli to oznacza klonowanie, to tak, jeśli jest lepszy sposób, to nie. – mpen

Odpowiedz

8

Chyba powinienem sklonować to pierwszy? Jak mogę to zrobić?

var universe = new HashSet<int>(); 
var subset = new HashSet<int>(); 
... 

// clone the universe 
var remaining = new HashSet<int>(universe); 
remaining.ExceptWith(subset); 

nie tak proste, jak przy metodzie Except przedłużacza, ale prawdopodobnie szybciej (należy uruchomić kilka testów wydajności, aby upewnić)

+0

Niestety, użyty przez Ciebie plik '' nowy HashSet (IEnumerable ) '] (https://msdn.microsoft.com/en-us/library/bb301504.aspx) nie wykorzystuje faktu, że istniejący zestaw zawiera tylko różne elementy i wywołuje kosztowną metodę "Dodaj (przedmiot)" dla każdego pojedynczego przedmiotu, zamiast efektywnie płytko klonować wewnętrzne struktury danych, tj. z coraz większym "wszechświatem", to staje się znacznie wolniejsze, niż mogłoby być. Stąd: +1 dla twojego pytania uzupełniającego: [Skuteczny sposób klonowania HashSet ?] (Http://stackoverflow.com/q/3927789/709537) –

9

Co powiecie na Except()?

var x = new HashSet<int>(); 
var y = new HashSet<int>(); 

var xminusy = new HashSet<int>(x.Except(y)); 
+0

Ale 'Z wyjątkiem' jest metodą rozszerzenia,' ExceptWith' jest specjalnie stworzone do pracy z 'HashSet' ... czy jest tak samo wydajne? – mpen

+1

@Mark, jest zdecydowanie mniej wydajny niż * tylko * 'ExceptWith', ale jest tak skuteczny jak * klonowanie * najpierw, a następnie wywołanie' ExceptWith'. –

+1

@Kirk: W końcu udało mi się to przetestować. Nie prawda. To wciąż około 40% wolniej. http://programanddesign.com/cs/subtracting-sets/ – mpen

1

Zestaw hash musi śledzić swoje stałe algorytmu mieszania i jego pojemniki przelewowe. Elementy w zestawie są przechowywane przez odniesienie. Tworzenie nowego skrótu z konstruktorem kopiującym, jak sugeruje Thomas Levesque, tworzy płytką kopię tego narzutu i powinno być dość szybkie. Używanie wyjątku() w sposób sugerowany przez Jamesa McNellisa tworzy najpierw anonimową kopię, a następnie przekazuje ją do konstruktora kopii, który używa pól w anonimowym, aby zainicjować własne pola. Jak powiedział Thomas, możesz wykonać kilka testów wydajności, ale teoretycznie jego odpowiedź powinna pokonać odpowiedź Jamesa. A tak na marginesie, na mój sposób myślenia płytka kopia nie jest klonem, ponieważ uważam, że klon oznacza, że ​​podstawowe elementy są również kopiowane. Zestawy skrótów ze wspólnymi elementami używają kopii w zmodyfikowanej strategii.

+0

Tak, masz rację, nie sądzę I tak potrzebuję głębokiej kopii. Używanie int w tym przykładzie, ale będą to klasy w praktyce; odniesienie jest jednak w porządku. – mpen

4

Przeprowadziłem benchmarking metody Linqa Except przeciwko klonowaniu i użyciu funkcji natywnej w HashSet ExceptWith. Oto wyniki.

static class Program 
{ 
    public static HashSet<T> ToSet<T>(this IEnumerable<T> collection) 
    { 
     return new HashSet<T>(collection); 
    } 

    public static HashSet<T> Subtract<T>(this HashSet<T> set, IEnumerable<T> other) 
    { 
     var clone = set.ToSet(); 
     clone.ExceptWith(other); 
     return clone; 
    } 

    static void Main(string[] args) 
    { 
     var A = new HashSet<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
     var B = new HashSet<int> { 2, 4, 6, 8, 10 }; 
     var sw = new Stopwatch(); 

     sw.Restart(); 
     for (int i = 0; i < 1000000; ++i) 
     { 
      var C = A.Except(B).ToSet(); 
     } 
     sw.Stop(); 
     Console.WriteLine("Linq: {0} ms", sw.ElapsedMilliseconds); 

     sw.Restart(); 
     for (int i = 0; i < 1000000; ++i) 
     { 
      var C = A.Subtract(B); 
     } 
     sw.Stop(); 
     Console.WriteLine("Native: {0} ms", sw.ElapsedMilliseconds); 

     Console.ReadLine(); 
    } 
} 

Linq: 1297 ms
Native: 762 ms

http://programanddesign.com/cs/subtracting-sets/

0

Bardzo późna odpowiedź, ale może czasami użyteczne.

@mpen odpowiedział przy użyciu LINQ, z wyjątkiem (IEnumerable <>)

które sprawiają LINQ pętla koryta IEnumerable sprawdzenie czy to zawiera.

Jak o

setA.Where (i =>! SetB.Contains (i))