1
|
/*
|
2
|
Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
|
3
|
|
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:
|
10
|
|
11
|
The above copyright notice and this permission notice shall be included in
|
12
|
all copies or substantial portions of the Software.
|
13
|
|
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
|
20
|
SOFTWARE.
|
21
|
*/
|
22
|
|
23
|
// C5 example: 2006-01-29, 2006-06-26
|
24
|
|
25
|
// Compile with
|
26
|
// csc /r:C5.dll MultiDictionary.cs
|
27
|
|
28
|
using System;
|
29
|
using C5;
|
30
|
using SCG = System.Collections.Generic;
|
31
|
|
32
|
namespace MultiDictionaries {
|
33
|
|
34
|
class MyTest {
|
35
|
public static void Main(String[] args) {
|
36
|
MultiDictionary1.TestIt.Run();
|
37
|
MultiDictionary2.TestIt.Run();
|
38
|
}
|
39
|
}
|
40
|
}
|
41
|
|
42
|
// --------------------------------------------------
|
43
|
|
44
|
namespace MultiDictionary1 {
|
45
|
class TestIt {
|
46
|
public static void Run() {
|
47
|
{
|
48
|
MultiHashDictionary<int,String> mdict
|
49
|
= new MultiHashDictionary<int,String>();
|
50
|
mdict.Add(2, "to");
|
51
|
mdict.Add(2, "deux");
|
52
|
mdict.Add(2, "two");
|
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>();
|
65
|
zwei.Add("zwei");
|
66
|
mdict[2] = zwei;
|
67
|
mdict[-2] = zwei;
|
68
|
Console.WriteLine(mdict);
|
69
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
70
|
zwei.Add("kaksi");
|
71
|
Console.WriteLine(mdict);
|
72
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
73
|
ICollection<String> empty = new HashSet<String>();
|
74
|
mdict[0] = empty;
|
75
|
Console.WriteLine(mdict);
|
76
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
77
|
Console.WriteLine("mdict contains key 0: {0}", mdict.Contains(0));
|
78
|
mdict.Remove(-2);
|
79
|
Console.WriteLine(mdict);
|
80
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
81
|
zwei.Remove("kaksi");
|
82
|
Console.WriteLine(mdict);
|
83
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
84
|
zwei.Clear();
|
85
|
Console.WriteLine(mdict);
|
86
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
87
|
Console.WriteLine("------------------------------");
|
88
|
}
|
89
|
}
|
90
|
}
|
91
|
|
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.
|
95
|
|
96
|
// The value collections are externally modifiable (as in Peter
|
97
|
// Golde's PowerCollections library), and therefore:
|
98
|
//
|
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.
|
103
|
//
|
104
|
// * A value collection may be shared between two or more keys.
|
105
|
//
|
106
|
|
107
|
public class MultiHashDictionary<K,V> : HashDictionary<K, ICollection<V>> {
|
108
|
|
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.
|
112
|
|
113
|
public new virtual int Count {
|
114
|
get {
|
115
|
int count = 0;
|
116
|
foreach (KeyValuePair<K,ICollection<V>> entry in this)
|
117
|
if (entry.Value != null)
|
118
|
count += entry.Value.Count;
|
119
|
return count;
|
120
|
}
|
121
|
}
|
122
|
|
123
|
public override Speed CountSpeed {
|
124
|
get { return Speed.Linear; }
|
125
|
}
|
126
|
|
127
|
// Add a (key,value) pair
|
128
|
|
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>();
|
133
|
Add(k, values);
|
134
|
}
|
135
|
values.Add(v);
|
136
|
}
|
137
|
|
138
|
// Remove a single (key,value) pair, if present; return true if
|
139
|
// anything was removed, else false
|
140
|
|
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)) {
|
145
|
if (values.IsEmpty)
|
146
|
base.Remove(k);
|
147
|
return true;
|
148
|
}
|
149
|
}
|
150
|
return false;
|
151
|
}
|
152
|
|
153
|
// Determine whether key k is associated with a value
|
154
|
|
155
|
public override bool Contains(K k) {
|
156
|
ICollection<V> values;
|
157
|
return base.Find(k, out values) && values != null && !values.IsEmpty;
|
158
|
}
|
159
|
|
160
|
// Determine whether each key in ks is associated with a value
|
161
|
|
162
|
public override bool ContainsAll<U>(SCG.IEnumerable<U> ks) {
|
163
|
foreach (K k in ks)
|
164
|
if (!Contains(k))
|
165
|
return false;
|
166
|
return true;
|
167
|
}
|
168
|
|
169
|
// Get or set the value collection associated with key k
|
170
|
|
171
|
public override ICollection<V> this[K k] {
|
172
|
get {
|
173
|
ICollection<V> values;
|
174
|
return base.Find(k, out values) && values != null ? values : new HashSet<V>();
|
175
|
}
|
176
|
set {
|
177
|
base[k] = value;
|
178
|
}
|
179
|
}
|
180
|
|
181
|
// Inherited from base class HashDictionary<K,ICollection<V>>:
|
182
|
|
183
|
// Add(K k, ICollection<V> values)
|
184
|
// AddAll(IEnumerable<KeyValuePair<K,ICollection<V>>> kvs)
|
185
|
// Clear
|
186
|
// Clone
|
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)
|
190
|
// Remove(K k)
|
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)
|
196
|
}
|
197
|
}
|
198
|
|
199
|
// --------------------------------------------------
|
200
|
|
201
|
namespace MultiDictionary2 {
|
202
|
|
203
|
class TestIt {
|
204
|
public static void Run() {
|
205
|
{
|
206
|
MultiHashDictionary<int,String> mdict
|
207
|
= new MultiHashDictionary<int,String>();
|
208
|
mdict.Add(2, "to");
|
209
|
mdict.Add(2, "deux");
|
210
|
mdict.Add(2, "two");
|
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>();
|
223
|
zwei.Add("zwei");
|
224
|
mdict[2] = zwei;
|
225
|
mdict[-2] = zwei;
|
226
|
Console.WriteLine(mdict);
|
227
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
228
|
zwei.Add("kaksi");
|
229
|
Console.WriteLine(mdict);
|
230
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
231
|
ICollection<String> empty = new HashSet<String>();
|
232
|
mdict[0] = empty;
|
233
|
Console.WriteLine(mdict);
|
234
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
235
|
Console.WriteLine("mdict contains key 0: {0}", mdict.Contains(0));
|
236
|
mdict.Remove(-2);
|
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);
|
242
|
zwei.Clear();
|
243
|
Console.WriteLine(mdict);
|
244
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
245
|
Console.WriteLine("------------------------------");
|
246
|
}
|
247
|
{
|
248
|
MultiHashDictionary<int,String,HashSet<String>> mdict
|
249
|
= new MultiHashDictionary<int,String,HashSet<String>>();
|
250
|
mdict.Add(2, "to");
|
251
|
mdict.Add(2, "deux");
|
252
|
mdict.Add(2, "two");
|
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>();
|
265
|
zwei.Add("zwei");
|
266
|
mdict[2] = zwei;
|
267
|
mdict[-2] = zwei;
|
268
|
Console.WriteLine(mdict);
|
269
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
270
|
zwei.Add("kaksi");
|
271
|
Console.WriteLine(mdict);
|
272
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
273
|
HashSet<String> empty = new HashSet<String>();
|
274
|
mdict[0] = empty;
|
275
|
Console.WriteLine(mdict);
|
276
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
277
|
Console.WriteLine("mdict contains key 0: {0}", mdict.Contains(0));
|
278
|
mdict.Remove(-2);
|
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);
|
284
|
zwei.Clear();
|
285
|
Console.WriteLine(mdict);
|
286
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
287
|
Console.WriteLine("------------------------------");
|
288
|
}
|
289
|
}
|
290
|
}
|
291
|
|
292
|
// This version of the multidictionary uses event listeners to make
|
293
|
// the Count operation constant time.
|
294
|
|
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.
|
299
|
|
300
|
public class MultiHashDictionary<K,V> : HashDictionary<K, ICollection<V>> {
|
301
|
private int count = 0; // Cached value count, updated by events only
|
302
|
|
303
|
private void IncrementCount(Object sender, ItemCountEventArgs<V> args) {
|
304
|
count += args.Count;
|
305
|
}
|
306
|
|
307
|
private void DecrementCount(Object sender, ItemCountEventArgs<V> args) {
|
308
|
count -= args.Count;
|
309
|
}
|
310
|
|
311
|
private void ClearedCount(Object sender, ClearedEventArgs args) {
|
312
|
count -= args.Count;
|
313
|
}
|
314
|
|
315
|
public MultiHashDictionary() {
|
316
|
ItemsAdded +=
|
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;
|
324
|
}
|
325
|
};
|
326
|
ItemsRemoved +=
|
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;
|
334
|
}
|
335
|
};
|
336
|
}
|
337
|
|
338
|
// Return total count of values associated with keys.
|
339
|
|
340
|
public new virtual int Count {
|
341
|
get {
|
342
|
return count;
|
343
|
}
|
344
|
}
|
345
|
|
346
|
public override Speed CountSpeed {
|
347
|
get { return Speed.Constant; }
|
348
|
}
|
349
|
|
350
|
// Add a (key,value) pair
|
351
|
|
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>();
|
356
|
Add(k, values);
|
357
|
}
|
358
|
values.Add(v);
|
359
|
}
|
360
|
|
361
|
// Remove a single (key,value) pair, if present; return true if
|
362
|
// anything was removed, else false
|
363
|
|
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)) {
|
368
|
if (values.IsEmpty)
|
369
|
base.Remove(k);
|
370
|
return true;
|
371
|
}
|
372
|
}
|
373
|
return false;
|
374
|
}
|
375
|
|
376
|
// Determine whether key k is associated with a value
|
377
|
|
378
|
public override bool Contains(K k) {
|
379
|
ICollection<V> values;
|
380
|
return Find(k, out values) && values != null && !values.IsEmpty;
|
381
|
}
|
382
|
|
383
|
// Determine whether each key in ks is associated with a value
|
384
|
|
385
|
public override bool ContainsAll<U>(SCG.IEnumerable<U> ks) {
|
386
|
foreach (K k in ks)
|
387
|
if (!Contains(k))
|
388
|
return false;
|
389
|
return true;
|
390
|
}
|
391
|
|
392
|
// Get or set the value collection associated with key k
|
393
|
|
394
|
public override ICollection<V> this[K k] {
|
395
|
get {
|
396
|
ICollection<V> values;
|
397
|
return base.Find(k, out values) && values != null ? values : new HashSet<V>();
|
398
|
}
|
399
|
set {
|
400
|
base[k] = value;
|
401
|
}
|
402
|
}
|
403
|
|
404
|
// Clearing the multidictionary should remove event listeners
|
405
|
|
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;
|
413
|
}
|
414
|
base.Clear();
|
415
|
}
|
416
|
}
|
417
|
|
418
|
// --------------------------------------------------
|
419
|
|
420
|
// This version of the multidictionary also uses event listeners to
|
421
|
// make the Count operation constant time.
|
422
|
|
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.
|
430
|
|
431
|
public class MultiHashDictionary<K,V,VC> : HashDictionary<K, VC>
|
432
|
where VC : ICollection<V>, new()
|
433
|
{
|
434
|
private int count = 0; // Cached value count, updated by events only
|
435
|
|
436
|
private void IncrementCount(Object sender, ItemCountEventArgs<V> args) {
|
437
|
count += args.Count;
|
438
|
}
|
439
|
|
440
|
private void DecrementCount(Object sender, ItemCountEventArgs<V> args) {
|
441
|
count -= args.Count;
|
442
|
}
|
443
|
|
444
|
private void ClearedCount(Object sender, ClearedEventArgs args) {
|
445
|
count -= args.Count;
|
446
|
}
|
447
|
|
448
|
public MultiHashDictionary() {
|
449
|
ItemsAdded +=
|
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;
|
457
|
}
|
458
|
};
|
459
|
ItemsRemoved +=
|
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;
|
467
|
}
|
468
|
};
|
469
|
}
|
470
|
|
471
|
// Return total count of values associated with keys.
|
472
|
|
473
|
public new virtual int Count {
|
474
|
get {
|
475
|
return count;
|
476
|
}
|
477
|
}
|
478
|
|
479
|
public override Speed CountSpeed {
|
480
|
get { return Speed.Constant; }
|
481
|
}
|
482
|
|
483
|
// Add a (key,value) pair
|
484
|
|
485
|
public virtual void Add(K k, V v) {
|
486
|
VC values;
|
487
|
if (!base.Find(k, out values) || values == null) {
|
488
|
values = new VC();
|
489
|
Add(k, values);
|
490
|
}
|
491
|
values.Add(v);
|
492
|
}
|
493
|
|
494
|
// Remove a single (key,value) pair, if present; return true if
|
495
|
// anything was removed, else false
|
496
|
|
497
|
public virtual bool Remove(K k, V v) {
|
498
|
VC values;
|
499
|
if (base.Find(k, out values) && values != null) {
|
500
|
if (values.Remove(v)) {
|
501
|
if (values.IsEmpty)
|
502
|
base.Remove(k);
|
503
|
return true;
|
504
|
}
|
505
|
}
|
506
|
return false;
|
507
|
}
|
508
|
|
509
|
// Determine whether key k is associated with a value
|
510
|
|
511
|
public override bool Contains(K k) {
|
512
|
VC values;
|
513
|
return Find(k, out values) && values != null && !values.IsEmpty;
|
514
|
}
|
515
|
|
516
|
// Determine whether each key in ks is associated with a value
|
517
|
|
518
|
public override bool ContainsAll<U>(SCG.IEnumerable<U> ks) {
|
519
|
foreach (K k in ks)
|
520
|
if (!Contains(k))
|
521
|
return false;
|
522
|
return true;
|
523
|
}
|
524
|
|
525
|
// Get or set the value collection associated with key k
|
526
|
|
527
|
public override VC this[K k] {
|
528
|
get {
|
529
|
VC values;
|
530
|
return base.Find(k, out values) && values != null ? values : new VC();
|
531
|
}
|
532
|
set {
|
533
|
base[k] = value;
|
534
|
}
|
535
|
}
|
536
|
|
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;
|
544
|
}
|
545
|
base.Clear();
|
546
|
}
|
547
|
}
|
548
|
}
|