2 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // C5 example: 2006-01-29, 2006-06-26
26 // csc /r:C5.dll MultiDictionary.cs
30 using SCG
= System
.Collections
.Generic
;
32 namespace MultiDictionaries
{
35 public static void Main(String
[] args
) {
36 MultiDictionary1
.TestIt
.Run();
37 MultiDictionary2
.TestIt
.Run();
42 // --------------------------------------------------
44 namespace MultiDictionary1
{
46 public static void Run() {
48 MultiHashDictionary
<int,String
> mdict
49 = new MultiHashDictionary
<int,String
>();
53 mdict
.Add(20, "tyve");
54 mdict
.Add(20, "twenty");
55 Console
.WriteLine(mdict
);
56 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
57 Console
.WriteLine("mdict.Count (keys) is {0}",
58 ((IDictionary
<int,ICollection
<String
>>)mdict
).Count
);
59 Console
.WriteLine("mdict[2].Count is {0}", mdict
[2].Count
);
60 mdict
.Remove(20, "tyve");
61 mdict
.Remove(20, "twenty");
62 Console
.WriteLine(mdict
);
63 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
64 ICollection
<String
> zwei
= new HashSet
<String
>();
68 Console
.WriteLine(mdict
);
69 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
71 Console
.WriteLine(mdict
);
72 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
73 ICollection
<String
> empty
= new HashSet
<String
>();
75 Console
.WriteLine(mdict
);
76 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
77 Console
.WriteLine("mdict contains key 0: {0}", mdict
.Contains(0));
79 Console
.WriteLine(mdict
);
80 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
82 Console
.WriteLine(mdict
);
83 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
85 Console
.WriteLine(mdict
);
86 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
87 Console
.WriteLine("------------------------------");
92 // Here we implement a multivalued dictionary as a hash dictionary
93 // from keys to value collections. The value collections may have
94 // set or bag semantics.
96 // The value collections are externally modifiable (as in Peter
97 // Golde's PowerCollections library), and therefore:
99 // * A value collection associated with a key may be null or
100 // non-empty. Hence for correct semantics, the Contains(k) method
101 // must check that the value collection associated with a key is
102 // non-null and non-empty.
104 // * A value collection may be shared between two or more keys.
107 public class MultiHashDictionary
<K
,V
> : HashDictionary
<K
, ICollection
<V
>> {
109 // Return total count of values associated with keys. This basic
110 // implementation simply sums over all value collections, and so
111 // is a linear-time operation in the total number of values.
113 public new virtual int Count
{
116 foreach (KeyValuePair
<K
,ICollection
<V
>> entry
in this)
117 if (entry
.Value
!= null)
118 count
+= entry
.Value
.Count
;
123 public override Speed CountSpeed
{
124 get { return Speed.Linear; }
127 // Add a (key,value) pair
129 public virtual void Add(K k
, V v
) {
130 ICollection
<V
> values
;
131 if (!base.Find(k
, out values
) || values
== null) {
132 values
= new HashSet
<V
>();
138 // Remove a single (key,value) pair, if present; return true if
139 // anything was removed, else false
141 public virtual bool Remove(K k
, V v
) {
142 ICollection
<V
> values
;
143 if (base.Find(k
, out values
) && values
!= null) {
144 if (values
.Remove(v
)) {
153 // Determine whether key k is associated with a value
155 public override bool Contains(K k
) {
156 ICollection
<V
> values
;
157 return base.Find(k
, out values
) && values
!= null && !values
.IsEmpty
;
160 // Determine whether each key in ks is associated with a value
162 public override bool ContainsAll
<U
>(SCG
.IEnumerable
<U
> ks
) {
169 // Get or set the value collection associated with key k
171 public override ICollection
<V
> this[K k
] {
173 ICollection
<V
> values
;
174 return base.Find(k
, out values
) && values
!= null ? values
: new HashSet
<V
>();
181 // Inherited from base class HashDictionary<K,ICollection<V>>:
183 // Add(K k, ICollection<V> values)
184 // AddAll(IEnumerable<KeyValuePair<K,ICollection<V>>> kvs)
187 // Find(K k, out ICollection<V> values)
188 // Find(ref K k, out ICollection<V> values)
189 // FindOrAdd(K k, ref ICollection<V> values)
191 // Remove(K k, out ICollection<V> values)
192 // Update(K k, ICollection<V> values)
193 // Update(K k, ICollection<V> values, out ICollection<V> oldValues)
194 // UpdateOrAdd(K k, ICollection<V> values)
195 // UpdateOrAdd(K k, ICollection<V> values, out ICollection<V> oldValues)
199 // --------------------------------------------------
201 namespace MultiDictionary2
{
204 public static void Run() {
206 MultiHashDictionary
<int,String
> mdict
207 = new MultiHashDictionary
<int,String
>();
209 mdict
.Add(2, "deux");
211 mdict
.Add(20, "tyve");
212 mdict
.Add(20, "twenty");
213 Console
.WriteLine(mdict
);
214 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
215 Console
.WriteLine("mdict.Count (keys) is {0}",
216 ((IDictionary
<int,ICollection
<String
>>)mdict
).Count
);
217 Console
.WriteLine("mdict[2].Count is {0}", mdict
[2].Count
);
218 mdict
.Remove(20, "tyve");
219 mdict
.Remove(20, "twenty");
220 Console
.WriteLine(mdict
);
221 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
222 ICollection
<String
> zwei
= new HashSet
<String
>();
226 Console
.WriteLine(mdict
);
227 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
229 Console
.WriteLine(mdict
);
230 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
231 ICollection
<String
> empty
= new HashSet
<String
>();
233 Console
.WriteLine(mdict
);
234 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
235 Console
.WriteLine("mdict contains key 0: {0}", mdict
.Contains(0));
237 Console
.WriteLine(mdict
);
238 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
239 zwei
.Remove("kaksi");
240 Console
.WriteLine(mdict
);
241 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
243 Console
.WriteLine(mdict
);
244 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
245 Console
.WriteLine("------------------------------");
248 MultiHashDictionary
<int,String
,HashSet
<String
>> mdict
249 = new MultiHashDictionary
<int,String
,HashSet
<String
>>();
251 mdict
.Add(2, "deux");
253 mdict
.Add(20, "tyve");
254 mdict
.Add(20, "twenty");
255 Console
.WriteLine(mdict
);
256 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
257 Console
.WriteLine("mdict.Count (keys) is {0}",
258 ((IDictionary
<int,HashSet
<String
>>)mdict
).Count
);
259 Console
.WriteLine("mdict[2].Count is {0}", mdict
[2].Count
);
260 mdict
.Remove(20, "tyve");
261 mdict
.Remove(20, "twenty");
262 Console
.WriteLine(mdict
);
263 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
264 HashSet
<String
> zwei
= new HashSet
<String
>();
268 Console
.WriteLine(mdict
);
269 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
271 Console
.WriteLine(mdict
);
272 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
273 HashSet
<String
> empty
= new HashSet
<String
>();
275 Console
.WriteLine(mdict
);
276 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
277 Console
.WriteLine("mdict contains key 0: {0}", mdict
.Contains(0));
279 Console
.WriteLine(mdict
);
280 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
281 zwei
.Remove("kaksi");
282 Console
.WriteLine(mdict
);
283 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
285 Console
.WriteLine(mdict
);
286 Console
.WriteLine("mdict.Count is {0}", mdict
.Count
);
287 Console
.WriteLine("------------------------------");
292 // This version of the multidictionary uses event listeners to make
293 // the Count operation constant time.
295 // The total value count for the multidictionary is cached, and
296 // event listeners on the value collections keep this cached count
297 // updated. Event listeners on the dictionary make sure that event
298 // listeners are added to and removed from value collections.
300 public class MultiHashDictionary
<K
,V
> : HashDictionary
<K
, ICollection
<V
>> {
301 private int count
= 0; // Cached value count, updated by events only
303 private void IncrementCount(Object sender
, ItemCountEventArgs
<V
> args
) {
307 private void DecrementCount(Object sender
, ItemCountEventArgs
<V
> args
) {
311 private void ClearedCount(Object sender
, ClearedEventArgs args
) {
315 public MultiHashDictionary() {
317 delegate(Object sender
, ItemCountEventArgs
<KeyValuePair
<K
,ICollection
<V
>>> args
) {
318 ICollection
<V
> values
= args
.Item
.Value
;
319 if (values
!= null) {
320 count
+= values
.Count
;
321 values
.ItemsAdded
+= IncrementCount
;
322 values
.ItemsRemoved
+= DecrementCount
;
323 values
.CollectionCleared
+= ClearedCount
;
327 delegate(Object sender
, ItemCountEventArgs
<KeyValuePair
<K
,ICollection
<V
>>> args
) {
328 ICollection
<V
> values
= args
.Item
.Value
;
329 if (values
!= null) {
330 count
-= values
.Count
;
331 values
.ItemsAdded
-= IncrementCount
;
332 values
.ItemsRemoved
-= DecrementCount
;
333 values
.CollectionCleared
-= ClearedCount
;
338 // Return total count of values associated with keys.
340 public new virtual int Count
{
346 public override Speed CountSpeed
{
347 get { return Speed.Constant; }
350 // Add a (key,value) pair
352 public virtual void Add(K k
, V v
) {
353 ICollection
<V
> values
;
354 if (!base.Find(k
, out values
) || values
== null) {
355 values
= new HashSet
<V
>();
361 // Remove a single (key,value) pair, if present; return true if
362 // anything was removed, else false
364 public virtual bool Remove(K k
, V v
) {
365 ICollection
<V
> values
;
366 if (base.Find(k
, out values
) && values
!= null) {
367 if (values
.Remove(v
)) {
376 // Determine whether key k is associated with a value
378 public override bool Contains(K k
) {
379 ICollection
<V
> values
;
380 return Find(k
, out values
) && values
!= null && !values
.IsEmpty
;
383 // Determine whether each key in ks is associated with a value
385 public override bool ContainsAll
<U
>(SCG
.IEnumerable
<U
> ks
) {
392 // Get or set the value collection associated with key k
394 public override ICollection
<V
> this[K k
] {
396 ICollection
<V
> values
;
397 return base.Find(k
, out values
) && values
!= null ? values
: new HashSet
<V
>();
404 // Clearing the multidictionary should remove event listeners
406 public override void Clear() {
407 foreach (ICollection
<V
> values
in Values
)
408 if (values
!= null) {
409 count
-= values
.Count
;
410 values
.ItemsAdded
-= IncrementCount
;
411 values
.ItemsRemoved
-= DecrementCount
;
412 values
.CollectionCleared
-= ClearedCount
;
418 // --------------------------------------------------
420 // This version of the multidictionary also uses event listeners to
421 // make the Count operation constant time.
423 // The difference relative to the preceding version is that each
424 // value collection must be an instance of some type VS that has an
425 // argumentless constructor and that implements ICollection<V>.
426 // This provides additional flexibility: The creator of a
427 // multidictionary instance can determine the collection class VC
428 // used for value collections, instead of having to put up with the
429 // choice made by the multidictionary implementation.
431 public class MultiHashDictionary
<K
,V
,VC
> : HashDictionary
<K
, VC
>
432 where VC
: ICollection
<V
>, new()
434 private int count
= 0; // Cached value count, updated by events only
436 private void IncrementCount(Object sender
, ItemCountEventArgs
<V
> args
) {
440 private void DecrementCount(Object sender
, ItemCountEventArgs
<V
> args
) {
444 private void ClearedCount(Object sender
, ClearedEventArgs args
) {
448 public MultiHashDictionary() {
450 delegate(Object sender
, ItemCountEventArgs
<KeyValuePair
<K
,VC
>> args
) {
451 VC values
= args
.Item
.Value
;
452 if (values
!= null) {
453 count
+= values
.Count
;
454 values
.ItemsAdded
+= IncrementCount
;
455 values
.ItemsRemoved
+= DecrementCount
;
456 values
.CollectionCleared
+= ClearedCount
;
460 delegate(Object sender
, ItemCountEventArgs
<KeyValuePair
<K
,VC
>> args
) {
461 VC values
= args
.Item
.Value
;
462 if (values
!= null) {
463 count
-= values
.Count
;
464 values
.ItemsAdded
-= IncrementCount
;
465 values
.ItemsRemoved
-= DecrementCount
;
466 values
.CollectionCleared
-= ClearedCount
;
471 // Return total count of values associated with keys.
473 public new virtual int Count
{
479 public override Speed CountSpeed
{
480 get { return Speed.Constant; }
483 // Add a (key,value) pair
485 public virtual void Add(K k
, V v
) {
487 if (!base.Find(k
, out values
) || values
== null) {
494 // Remove a single (key,value) pair, if present; return true if
495 // anything was removed, else false
497 public virtual bool Remove(K k
, V v
) {
499 if (base.Find(k
, out values
) && values
!= null) {
500 if (values
.Remove(v
)) {
509 // Determine whether key k is associated with a value
511 public override bool Contains(K k
) {
513 return Find(k
, out values
) && values
!= null && !values
.IsEmpty
;
516 // Determine whether each key in ks is associated with a value
518 public override bool ContainsAll
<U
>(SCG
.IEnumerable
<U
> ks
) {
525 // Get or set the value collection associated with key k
527 public override VC
this[K k
] {
530 return base.Find(k
, out values
) && values
!= null ? values
: new VC();
537 public override void Clear() {
538 foreach (VC values
in Values
)
539 if (values
!= null) {
540 count
-= values
.Count
;
541 values
.ItemsAdded
-= IncrementCount
;
542 values
.ItemsRemoved
-= DecrementCount
;
543 values
.CollectionCleared
-= ClearedCount
;