1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.dishevelled.weighted;
25
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.List;
29 import java.util.Random;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.Comparator;
36 import java.util.AbstractSet;
37 import java.util.AbstractCollection;
38
39
40
41
42
43
44
45
46
47 public final class HashWeightedMap<E>
48 implements WeightedMap<E>
49 {
50
51 private final Map<E, Double> map;
52
53
54 private Double totalWeight = 0.0d;
55
56
57 private Random random = new Random();
58
59
60 private transient Map<E, Integer> rank;
61
62
63 private transient boolean dirty;
64
65
66 private transient EntrySet entrySet;
67
68
69 private transient KeySet keySet;
70
71
72 private transient Values values;
73
74
75 private static final int DEFAULT_INITIAL_CAPACITY = 16;
76
77
78 private static final float DEFAULT_LOAD_FACTOR = 0.75f;
79
80
81
82
83
84
85 public HashWeightedMap()
86 {
87 map = new HashMap<E, Double>();
88 dirty = true;
89 }
90
91
92
93
94
95
96
97 public HashWeightedMap(final int initialCapacity)
98 {
99 map = new HashMap<E, Double>(initialCapacity, DEFAULT_LOAD_FACTOR);
100 dirty = true;
101 }
102
103
104
105
106
107
108
109
110 public HashWeightedMap(final int initialCapacity, final float loadFactor)
111 {
112 map = new HashMap<E, Double>(initialCapacity, loadFactor);
113 dirty = true;
114 }
115
116
117
118
119
120
121
122 public HashWeightedMap(final WeightedMap<? extends E> weightedMap)
123 {
124 map = new HashMap<E, Double>(Math.max(2 * weightedMap.size(), DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
125 putAll(weightedMap);
126 }
127
128
129
130
131
132
133
134
135 public void setRandom(final Random random)
136 {
137 if (random == null)
138 {
139 throw new IllegalArgumentException("random must not be null");
140 }
141 this.random = random;
142 }
143
144
145 public void clear()
146 {
147 map.clear();
148 dirty = true;
149 totalWeight = 0.0d;
150 }
151
152
153 public int size()
154 {
155 return map.size();
156 }
157
158
159 public boolean isEmpty()
160 {
161 return map.isEmpty();
162 }
163
164
165 public boolean containsKey(final Object o)
166 {
167 return map.containsKey(o);
168 }
169
170
171 public boolean containsValue(final Object o)
172 {
173 return map.containsValue(o);
174 }
175
176
177 public Double get(final Object o)
178 {
179 return map.get(o);
180 }
181
182
183 public Double put(final E e, final Double w)
184 {
185
186 if (w < 0.0d)
187 {
188 throw new IllegalArgumentException("w must be >= 0.0d");
189 }
190
191 Double oldWeight = map.put(e, w);
192 if (oldWeight != null)
193 {
194 totalWeight -= oldWeight;
195 }
196 dirty = true;
197 totalWeight += w;
198 return oldWeight;
199 }
200
201
202 public void putAll(final Map<? extends E, ? extends Double> t)
203 {
204 for (Map.Entry<? extends E, ? extends Double> e : t.entrySet())
205 {
206 put(e.getKey(), e.getValue());
207 }
208 }
209
210
211 public Double remove(final Object o)
212 {
213 Double w = map.remove(o);
214 if (w != null)
215 {
216 dirty = true;
217 totalWeight -= w;
218 }
219 return w;
220 }
221
222
223 public E sample()
224 {
225 Double r = random.nextDouble();
226
227 for (E e : keySet())
228 {
229 r -= normalizedWeight(e);
230
231 if (r <= 0.0d)
232 {
233 return e;
234 }
235 }
236 return null;
237 }
238
239
240 public Double weight(final E e)
241 {
242 return map.get(e);
243 }
244
245
246 public Double normalizedWeight(final E e)
247 {
248 if (isEmpty())
249 {
250 return null;
251 }
252 if (totalWeight == 0.0d)
253 {
254 return 0.0d;
255 }
256
257 Double w = weight(e);
258
259 if (w == null)
260 {
261 return null;
262 }
263
264 return (w / totalWeight);
265 }
266
267
268 public Double totalWeight()
269 {
270 return totalWeight;
271 }
272
273
274 public int rank(final E e)
275 {
276 if (dirty)
277 {
278 calculateRank();
279 dirty = false;
280 }
281 return rank.containsKey(e) ? rank.get(e) : -1;
282 }
283
284
285 public int maximumRank()
286 {
287 if (isEmpty())
288 {
289 return -1;
290 }
291
292 int maximumRank = 0;
293 for (E e : keySet())
294 {
295 int currentRank = rank(e);
296
297 if (currentRank > maximumRank)
298 {
299 maximumRank = currentRank;
300 }
301 }
302
303 return maximumRank;
304 }
305
306
307
308
309
310 private void calculateRank()
311 {
312 rank = new HashMap<E, Integer>(size());
313
314 int r = 0;
315 List<E> l = new ArrayList<E>(keySet());
316 Collections.sort(l, byRankDescending);
317
318 Double lastWeight = Double.NaN;
319 for (E e : l)
320 {
321 Double w = normalizedWeight(e);
322 if (!lastWeight.equals(w))
323 {
324 r++;
325 }
326 rank.put(e, r);
327 lastWeight = w;
328 }
329 l = null;
330 }
331
332
333 public Set<E> keySet()
334 {
335 if (keySet == null)
336 {
337 keySet = new KeySet();
338 }
339 return keySet;
340 }
341
342
343 public Collection<Double> values()
344 {
345 if (values == null)
346 {
347 values = new Values();
348 }
349 return values;
350 }
351
352
353 public Set<Map.Entry<E, Double>> entrySet()
354 {
355 if (entrySet == null)
356 {
357 entrySet = new EntrySet();
358 }
359 return entrySet;
360 }
361
362
363
364
365
366 private transient final Comparator<E> byRankDescending = new Comparator<E>()
367 {
368
369 public int compare(final E e1, final E e2)
370 {
371 Double w1 = normalizedWeight(e1);
372 Double w2 = normalizedWeight(e2);
373
374 return (w2.compareTo(w1));
375 }
376 };
377
378
379
380
381 private class KeySet
382 extends AbstractSet<E>
383 {
384
385
386 public int size()
387 {
388 return map.keySet().size();
389 }
390
391
392 public void clear()
393 {
394 HashWeightedMap.this.clear();
395 }
396
397
398 public Iterator<E> iterator()
399 {
400 return new KeySetIterator();
401 }
402 }
403
404
405
406
407 private class KeySetIterator
408 implements Iterator<E>
409 {
410
411 private Iterator<E> iterator;
412
413
414 private E e;
415
416
417
418
419
420 public KeySetIterator()
421 {
422 iterator = map.keySet().iterator();
423 }
424
425
426
427 public boolean hasNext()
428 {
429 return iterator.hasNext();
430 }
431
432
433 public E next()
434 {
435 e = iterator.next();
436 return e;
437 }
438
439
440 public void remove()
441 {
442 Double w = weight(e);
443 iterator.remove();
444 dirty = true;
445 totalWeight -= w;
446 }
447 }
448
449
450
451
452 private class Values
453 extends AbstractCollection<Double>
454 {
455
456
457 public int size()
458 {
459 return map.values().size();
460 }
461
462
463 public void clear()
464 {
465 HashWeightedMap.this.clear();
466 }
467
468
469 public Iterator<Double> iterator()
470 {
471 return new ValuesIterator();
472 }
473 }
474
475
476
477
478 private class ValuesIterator
479 implements Iterator<Double>
480 {
481
482 private Iterator<Double> iterator;
483
484
485 private Double w;
486
487
488
489
490
491 public ValuesIterator()
492 {
493 iterator = map.values().iterator();
494 }
495
496
497
498 public boolean hasNext()
499 {
500 return iterator.hasNext();
501 }
502
503
504 public Double next()
505 {
506 w = iterator.next();
507 return w;
508 }
509
510
511 public void remove()
512 {
513 iterator.remove();
514 dirty = true;
515 totalWeight -= w;
516 }
517 }
518
519
520
521
522 private class EntrySet
523 extends AbstractSet<Map.Entry<E, Double>>
524 {
525
526
527 public int size()
528 {
529 return map.entrySet().size();
530 }
531
532
533 public void clear()
534 {
535 HashWeightedMap.this.clear();
536 }
537
538
539 public Iterator<Map.Entry<E, Double>> iterator()
540 {
541 return new EntrySetIterator();
542 }
543 }
544
545
546
547
548 private class EntrySetIterator
549 implements Iterator<Map.Entry<E, Double>>
550 {
551
552 private Iterator<Map.Entry<E, Double>> iterator;
553
554
555 private Map.Entry<E, Double> e;
556
557
558
559
560
561 public EntrySetIterator()
562 {
563 iterator = map.entrySet().iterator();
564 }
565
566
567
568 public boolean hasNext()
569 {
570 return iterator.hasNext();
571 }
572
573
574 public Map.Entry<E, Double> next()
575 {
576 e = iterator.next();
577 return new MapEntry(e);
578 }
579
580
581 public void remove()
582 {
583 iterator.remove();
584 dirty = true;
585 totalWeight -= e.getValue();
586 }
587 }
588
589
590
591
592 private class MapEntry
593 implements Map.Entry<E, Double>
594 {
595
596 private Map.Entry<E, Double> e;
597
598
599
600
601
602
603
604 public MapEntry(final Map.Entry<E, Double> e)
605 {
606 this.e = e;
607 }
608
609
610
611 public boolean equals(final Object o)
612 {
613 return e.equals(o);
614 }
615
616
617 public int hashCode()
618 {
619 return e.hashCode();
620 }
621
622
623 public E getKey()
624 {
625 return e.getKey();
626 }
627
628
629 public Double getValue()
630 {
631 return e.getValue();
632 }
633
634
635 public Double setValue(final Double w)
636 {
637
638 if (w < 0.0d)
639 {
640 throw new IllegalArgumentException("w must be >= 0.0d");
641 }
642
643 Double oldWeight = e.setValue(w);
644 if (oldWeight != null)
645 {
646 totalWeight -= oldWeight;
647 }
648 dirty = true;
649 totalWeight += w;
650 return oldWeight;
651 }
652 }
653 }