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
|
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
|
using Inner = HashSet<String>;
|
35
|
|
36
|
using MultiDictionary3;
|
37
|
|
38
|
public class MyTest
|
39
|
{
|
40
|
public static void Main()
|
41
|
{
|
42
|
MultiHashDictionary<int, String, Inner> mdict = new MultiHashDictionary<int, String, Inner>();
|
43
|
mdict.Add(2, "to");
|
44
|
mdict.Add(2, "deux");
|
45
|
mdict.Add(2, "two");
|
46
|
mdict.Add(20, "tyve");
|
47
|
mdict.Add(20, "tyve");
|
48
|
mdict.Add(20, "twenty");
|
49
|
Console.WriteLine(mdict);
|
50
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
51
|
Console.WriteLine("mdict.Count (keys) is {0}",
|
52
|
((IDictionary<int, Inner>)mdict).Count);
|
53
|
Console.WriteLine("mdict[2].Count is {0}", mdict[2].Count);
|
54
|
mdict.Remove(20, "tyve");
|
55
|
mdict.Remove(20, "twenty");
|
56
|
Console.WriteLine(mdict);
|
57
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
58
|
Inner zwei = new Inner();
|
59
|
zwei.Add("zwei");
|
60
|
mdict[2] = zwei;
|
61
|
mdict[-2] = zwei;
|
62
|
Console.WriteLine(mdict);
|
63
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
64
|
zwei.Add("kaksi");
|
65
|
Console.WriteLine(mdict);
|
66
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
67
|
Inner empty = new Inner();
|
68
|
mdict[0] = empty;
|
69
|
Console.WriteLine(mdict);
|
70
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
71
|
Console.WriteLine("mdict contains key 0: {0}", mdict.Contains(0));
|
72
|
mdict.Remove(-2);
|
73
|
Console.WriteLine(mdict);
|
74
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
75
|
zwei.Remove("kaksi");
|
76
|
Console.WriteLine(mdict);
|
77
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
78
|
zwei.Clear();
|
79
|
Console.WriteLine(mdict);
|
80
|
Console.WriteLine("mdict.Count is {0}", mdict.Count);
|
81
|
}
|
82
|
}
|
83
|
}
|
84
|
|
85
|
namespace MultiDictionary1
|
86
|
{
|
87
|
// Here we implement a multivalued dictionary as a hash dictionary
|
88
|
// from keys to value collections. The value collections may have set
|
89
|
// or bag semantics.
|
90
|
|
91
|
// The value collections are externally modifiable (as in Peter
|
92
|
// Golde's PowerCollections library), and therefore:
|
93
|
//
|
94
|
// * A value collection associated with a key may be null or
|
95
|
// non-empty. Hence for correct semantics, the Contains(k) method
|
96
|
// must check that the value collection associated with a key is
|
97
|
// non-null and non-empty.
|
98
|
//
|
99
|
// * A value collection may be shared between two or more keys.
|
100
|
//
|
101
|
|
102
|
// A C5 hash dictionary each of whose keys map to a collection of values.
|
103
|
|
104
|
public class MultiHashDictionary<K, V> : HashDictionary<K, ICollection<V>>
|
105
|
{
|
106
|
|
107
|
// Return total count of values associated with keys. This basic
|
108
|
// implementation simply sums over all value collections, and so
|
109
|
// is a linear-time operation in the total number of values.
|
110
|
|
111
|
public new virtual int Count
|
112
|
{
|
113
|
get
|
114
|
{
|
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
|
{
|
125
|
get { return Speed.Linear; }
|
126
|
}
|
127
|
|
128
|
// Add a (key,value) pair
|
129
|
|
130
|
public virtual void Add(K k, V v)
|
131
|
{
|
132
|
ICollection<V> values;
|
133
|
if (!base.Find(k, out values) || values == null)
|
134
|
{
|
135
|
values = new HashSet<V>();
|
136
|
Add(k, values);
|
137
|
}
|
138
|
values.Add(v);
|
139
|
}
|
140
|
|
141
|
// Remove a single (key,value) pair, if present; return true if
|
142
|
// anything was removed, else false
|
143
|
|
144
|
public virtual bool Remove(K k, V v)
|
145
|
{
|
146
|
ICollection<V> values;
|
147
|
if (base.Find(k, out values) && values != null)
|
148
|
{
|
149
|
if (values.Remove(v))
|
150
|
{
|
151
|
if (values.IsEmpty)
|
152
|
base.Remove(k);
|
153
|
return true;
|
154
|
}
|
155
|
}
|
156
|
return false;
|
157
|
}
|
158
|
|
159
|
// Determine whether key k is associated with a value
|
160
|
|
161
|
public override bool Contains(K k)
|
162
|
{
|
163
|
ICollection<V> values;
|
164
|
return Find(k, out values) && values != null && !values.IsEmpty;
|
165
|
}
|
166
|
|
167
|
// Determine whether each key in ks is associated with a value
|
168
|
|
169
|
public override bool ContainsAll<U>(SCG.IEnumerable<U> ks)
|
170
|
{
|
171
|
foreach (K k in ks)
|
172
|
if (!Contains(k))
|
173
|
return false;
|
174
|
return true;
|
175
|
}
|
176
|
|
177
|
// Get or set the value collection associated with key k
|
178
|
|
179
|
public override ICollection<V> this[K k]
|
180
|
{
|
181
|
get
|
182
|
{
|
183
|
ICollection<V> values;
|
184
|
return base.Find(k, out values) && values != null ? values : new HashSet<V>();
|
185
|
}
|
186
|
set
|
187
|
{
|
188
|
base[k] = value;
|
189
|
}
|
190
|
}
|
191
|
|
192
|
// Inherited from base class HashDictionary<K,ICollection<V>>:
|
193
|
|
194
|
// Add(K k, ICollection<V> values)
|
195
|
// AddAll(IEnumerable<KeyValuePair<K,ICollection<V>>> kvs)
|
196
|
// Clear
|
197
|
// Clone
|
198
|
// Find(K k, out ICollection<V> values)
|
199
|
// Find(ref K k, out ICollection<V> values)
|
200
|
// FindOrAdd(K k, ref ICollection<V> values)
|
201
|
// Remove(K k)
|
202
|
// Remove(K k, out ICollection<V> values)
|
203
|
// Update(K k, ICollection<V> values)
|
204
|
// Update(K k, ICollection<V> values, out ICollection<V> oldValues)
|
205
|
// UpdateOrAdd(K k, ICollection<V> values)
|
206
|
// UpdateOrAdd(K k, ICollection<V> values, out ICollection<V> oldValues)
|
207
|
}
|
208
|
}
|
209
|
|
210
|
|
211
|
namespace MultiDictionary2
|
212
|
{
|
213
|
// Here we implement a multivalued dictionary as a hash dictionary
|
214
|
// from keys to value collections. The value collections may have
|
215
|
// set or bag semantics. This version uses event listeners to make
|
216
|
// the Count operation constant time.
|
217
|
|
218
|
// * To avoid recomputing the total number of values for the Count
|
219
|
// property, one may cache it and then use event listeners on the
|
220
|
// value collections to keep the cached count updated. In turn, this
|
221
|
// requires such event listeners on the dictionary also.
|
222
|
|
223
|
// A C5 hash dictionary each of whose keys map to a collection of values.
|
224
|
|
225
|
public class MultiHashDictionary<K, V> : HashDictionary<K, ICollection<V>>
|
226
|
{
|
227
|
private int count = 0; // Cached value count, updated by events only
|
228
|
|
229
|
private void IncrementCount(Object sender, ItemCountEventArgs<V> args)
|
230
|
{
|
231
|
count += args.Count;
|
232
|
}
|
233
|
|
234
|
private void DecrementCount(Object sender, ItemCountEventArgs<V> args)
|
235
|
{
|
236
|
count -= args.Count;
|
237
|
}
|
238
|
|
239
|
private void ClearedCount(Object sender, ClearedEventArgs args)
|
240
|
{
|
241
|
count -= args.Count;
|
242
|
}
|
243
|
|
244
|
public MultiHashDictionary()
|
245
|
{
|
246
|
ItemsAdded +=
|
247
|
delegate(Object sender, ItemCountEventArgs<KeyValuePair<K, ICollection<V>>> args)
|
248
|
{
|
249
|
ICollection<V> values = args.Item.Value;
|
250
|
if (values != null)
|
251
|
{
|
252
|
count += values.Count;
|
253
|
values.ItemsAdded += IncrementCount;
|
254
|
values.ItemsRemoved += DecrementCount;
|
255
|
values.CollectionCleared += ClearedCount;
|
256
|
}
|
257
|
};
|
258
|
ItemsRemoved +=
|
259
|
delegate(Object sender, ItemCountEventArgs<KeyValuePair<K, ICollection<V>>> args)
|
260
|
{
|
261
|
ICollection<V> values = args.Item.Value;
|
262
|
if (values != null)
|
263
|
{
|
264
|
count -= values.Count;
|
265
|
values.ItemsAdded -= IncrementCount;
|
266
|
values.ItemsRemoved -= DecrementCount;
|
267
|
values.CollectionCleared -= ClearedCount;
|
268
|
}
|
269
|
};
|
270
|
}
|
271
|
|
272
|
// Return total count of values associated with keys.
|
273
|
|
274
|
public new virtual int Count
|
275
|
{
|
276
|
get
|
277
|
{
|
278
|
return count;
|
279
|
}
|
280
|
}
|
281
|
|
282
|
public override Speed CountSpeed
|
283
|
{
|
284
|
get { return Speed.Constant; }
|
285
|
}
|
286
|
|
287
|
// Add a (key,value) pair
|
288
|
|
289
|
public virtual void Add(K k, V v)
|
290
|
{
|
291
|
ICollection<V> values;
|
292
|
if (!base.Find(k, out values) || values == null)
|
293
|
{
|
294
|
values = new HashSet<V>();
|
295
|
Add(k, values);
|
296
|
}
|
297
|
values.Add(v);
|
298
|
}
|
299
|
|
300
|
// Remove a single (key,value) pair, if present; return true if
|
301
|
// anything was removed, else false
|
302
|
|
303
|
public virtual bool Remove(K k, V v)
|
304
|
{
|
305
|
ICollection<V> values;
|
306
|
if (base.Find(k, out values) && values != null)
|
307
|
{
|
308
|
if (values.Remove(v))
|
309
|
{
|
310
|
if (values.IsEmpty)
|
311
|
base.Remove(k);
|
312
|
return true;
|
313
|
}
|
314
|
}
|
315
|
return false;
|
316
|
}
|
317
|
|
318
|
// Determine whether key k is associated with a value
|
319
|
|
320
|
public override bool Contains(K k)
|
321
|
{
|
322
|
ICollection<V> values;
|
323
|
return Find(k, out values) && values != null && !values.IsEmpty;
|
324
|
}
|
325
|
|
326
|
// Determine whether each key in ks is associated with a value
|
327
|
|
328
|
public override bool ContainsAll<U>(SCG.IEnumerable<U> ks)
|
329
|
{
|
330
|
foreach (K k in ks)
|
331
|
if (!Contains(k))
|
332
|
return false;
|
333
|
return true;
|
334
|
}
|
335
|
|
336
|
// Get or set the value collection associated with key k
|
337
|
|
338
|
public override ICollection<V> this[K k]
|
339
|
{
|
340
|
get
|
341
|
{
|
342
|
ICollection<V> values;
|
343
|
return base.Find(k, out values) && values != null ? values : new HashSet<V>();
|
344
|
}
|
345
|
set
|
346
|
{
|
347
|
base[k] = value;
|
348
|
}
|
349
|
}
|
350
|
}
|
351
|
}
|
352
|
|
353
|
|
354
|
namespace MultiDictionary3
|
355
|
{
|
356
|
// Here we implement a multivalued dictionary as a hash dictionary
|
357
|
// from keys to value collections. The value collections may have
|
358
|
// set or bag semantics. This version uses event listeners to make
|
359
|
// the Count operation constant time.
|
360
|
|
361
|
// * To avoid recomputing the total number of values for the Count
|
362
|
// property, one may cache it and then use event listeners on the
|
363
|
// value collections to keep the cached count updated. In turn, this
|
364
|
// requires such event listeners on the dictionary also.
|
365
|
|
366
|
// A C5 hash dictionary each of whose keys map to a collection of values.
|
367
|
|
368
|
public class MultiHashDictionary<K, V, W> : HashDictionary<K, W> where W : ICollection<V>, new()
|
369
|
{
|
370
|
private int count = 0; // Cached value count, updated by events only
|
371
|
|
372
|
private void IncrementCount(Object sender, ItemCountEventArgs<V> args)
|
373
|
{
|
374
|
count += args.Count;
|
375
|
}
|
376
|
|
377
|
private void DecrementCount(Object sender, ItemCountEventArgs<V> args)
|
378
|
{
|
379
|
count -= args.Count;
|
380
|
}
|
381
|
|
382
|
private void ClearedCount(Object sender, ClearedEventArgs args)
|
383
|
{
|
384
|
count -= args.Count;
|
385
|
}
|
386
|
|
387
|
public MultiHashDictionary()
|
388
|
{
|
389
|
ItemsAdded +=
|
390
|
delegate(Object sender, ItemCountEventArgs<KeyValuePair<K, W>> args)
|
391
|
{
|
392
|
ICollection<V> values = args.Item.Value;
|
393
|
if (values != null)
|
394
|
{
|
395
|
count += values.Count;
|
396
|
values.ItemsAdded += IncrementCount;
|
397
|
values.ItemsRemoved += DecrementCount;
|
398
|
values.CollectionCleared += ClearedCount;
|
399
|
}
|
400
|
};
|
401
|
ItemsRemoved +=
|
402
|
delegate(Object sender, ItemCountEventArgs<KeyValuePair<K, W>> args)
|
403
|
{
|
404
|
ICollection<V> values = args.Item.Value;
|
405
|
if (values != null)
|
406
|
{
|
407
|
count -= values.Count;
|
408
|
values.ItemsAdded -= IncrementCount;
|
409
|
values.ItemsRemoved -= DecrementCount;
|
410
|
values.CollectionCleared -= ClearedCount;
|
411
|
}
|
412
|
};
|
413
|
}
|
414
|
|
415
|
// Return total count of values associated with keys.
|
416
|
|
417
|
public new virtual int Count
|
418
|
{
|
419
|
get
|
420
|
{
|
421
|
return count;
|
422
|
}
|
423
|
}
|
424
|
|
425
|
public override Speed CountSpeed
|
426
|
{
|
427
|
get { return Speed.Constant; }
|
428
|
}
|
429
|
|
430
|
// Add a (key,value) pair
|
431
|
|
432
|
public virtual void Add(K k, V v)
|
433
|
{
|
434
|
W values;
|
435
|
if (!base.Find(k, out values) || values == null)
|
436
|
{
|
437
|
values = new W();
|
438
|
Add(k, values);
|
439
|
}
|
440
|
values.Add(v);
|
441
|
}
|
442
|
|
443
|
// Remove a single (key,value) pair, if present; return true if
|
444
|
// anything was removed, else false
|
445
|
|
446
|
public virtual bool Remove(K k, V v)
|
447
|
{
|
448
|
W values;
|
449
|
if (base.Find(k, out values) && values != null)
|
450
|
{
|
451
|
if (values.Remove(v))
|
452
|
{
|
453
|
if (values.IsEmpty)
|
454
|
base.Remove(k);
|
455
|
return true;
|
456
|
}
|
457
|
}
|
458
|
return false;
|
459
|
}
|
460
|
|
461
|
// Determine whether key k is associated with a value
|
462
|
|
463
|
public override bool Contains(K k)
|
464
|
{
|
465
|
W values;
|
466
|
return Find(k, out values) && values != null && !values.IsEmpty;
|
467
|
}
|
468
|
|
469
|
// Determine whether each key in ks is associated with a value
|
470
|
|
471
|
public override bool ContainsAll<U>(SCG.IEnumerable<U> ks)
|
472
|
{
|
473
|
foreach (K k in ks)
|
474
|
if (!Contains(k))
|
475
|
return false;
|
476
|
return true;
|
477
|
}
|
478
|
|
479
|
// Get or set the value collection associated with key k
|
480
|
|
481
|
public override W this[K k]
|
482
|
{
|
483
|
get
|
484
|
{
|
485
|
W values;
|
486
|
return base.Find(k, out values) && values != null ? values : new W();
|
487
|
}
|
488
|
set
|
489
|
{
|
490
|
base[k] = value;
|
491
|
}
|
492
|
}
|
493
|
}
|
494
|
}
|