C# i zbiory

2010-06-08 16:39

Dłuższy czas temu popełniłem notkę o tym, że w C# (lub szerzej: w .NET) brakuje pewnych struktur danych, które niekiedy bywają przydatne. Jedną z nich jest bardzo prosty rodzaj pojemnika: zbiór.
Zbiory (w programowaniu) to kontenery, które przechowują elementy niepowtarzające się i umożliwiają szybkie sprawdzenie, czy jakaś wartość do danego zbioru należy. ‘Szybkie’ oznacza tu złożoność logarytmiczną (względem rozmiaru pojemnika) lub lepszą. Podstawowa różnica w stosunku do zbiorów matematycznych jest natomiast taka, iż te drugie mogą zawierać elementy różnych rodzajów, podczas struktura danych o tej nazwie przechowuje obiekty jednego typu.

W C++ zbiór implementuje STL-owa klasa std::set. W C# z kolei – jak napisałem na początku – jej odpowiednika nie ma. Takowy trzeba by dopiero napisać, co w przypadku tego języka jest raczej zaskakujące :) Tym niemniej da się to zrobić w miarę prosto, używając do tego… klasy Dictionary. Pomysł polega na tym, żeby całkowicie zignorować tę jej część, która kluczom w słowniku pozwala przypisywać wartości. Zamiast tego interesują nas wyłącznie same klucze jako elementy naszego zbioru. Zarys klasy opartej o tę ideę może wyglądać choćby tak:

  1. public class Set<T> : ICollection<T>, IEnumerable<T>
  2. {
  3.     private Dictionary<T, object> set;
  4.  
  5.     public Set() { set = new Dictionary<T, object>(); }
  6.  
  7.     public void Add(T item) { set.Add(item, null); }
  8.     public void Clear() { set.Clear(); }
  9.     public bool Contains(T item) { return set.ContainsKey(item); }
  10.     public void CopyTo(T[] array, int arrayIndex)
  11.         { set.Keys.CopyTo(array, arrayIndex); }
  12.     public int Count    { get { return set.Count; } }
  13.     public bool IsReadOnly  { get { return false; } }
  14.     public bool Remove(T item) { return set.Remove(item); }
  15.  
  16.     public IEnumerator<T> GetEnumerator()
  17.         { return set.Keys.GetEnumerator(); }
  18.     IEnumerator IEnumerable.GetEnumerator()
  19.         { return (set.Keys as IEnumerable).GetEnumerator(); }
  20. }

Skorzystanie ze słownika (klasy Dictionary) sprawia, że kluczowa operacja Contains (sprawdzenie przynależności) jest bardzo szybka. MSDN podaje, że wydajność odpowiadającej jej operacji słownikowej ContainsKey “zbliża się do O(1)”. Mamy tu też oczywiście niezbędne metody do dodawania i usuwania elementów, a także kilka innych związanych z implementowanymi interfejsami ICollection i IEnumerable, które pozwalają na przykład na iterowanie po zbiorze pętlą foreach.
Można naturalnie jeszcze ulepszyć tę klasę, dodając nowe konstruktory, metody i implementując następne interfejsy (na przykład ISerializable). Nie jest to trudne, bo polega głównie na wywoływaniu odpowiadających metod obiektu Dictionary lub jego kolekcji kluczy. Dopiero operacje matematyczne – jak suma i iloczyn zbiorów – wymagałoby nieco większej ilości kodu.
Ale przecież dla chcącego nic trudnego :)

Tags: , ,
Author: Xion, posted under Programming »


4 comments for post “C# i zbiory”.
  1. KrystianD:
    June 8th, 2010 o 19:27

    nie było w v2.0 :) w v3.5 jest HashSet ;)

  2. Xion:
    June 8th, 2010 o 20:27

    Ha, no to najwyższy czas :) Widać od ostatniego razu, gdy potrzebowałem w C# zbioru, minęło już wystarczająco długo ;)

  3. nilphilus:
    June 8th, 2010 o 22:00

    Właśnie chciałem napisać, że lepiej HashSet użyć, no ale mnie ubiegli ;-)

  4. nilphilus:
    June 8th, 2010 o 22:03

    eniłej, nie można by LINQ do tego wykorzystać?

Comments are disabled.
 


© 2019 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.