View Javadoc

1   /*
2   
3       dsh-weighted  Weighted map interface and implementation.
4       Copyright (c) 2005-2011 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
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   * Implementation of WeightedMap that delegates to a HashMap.
41   *
42   * @param <E> the type of elements maintained by this weighted map
43   * @author  Michael Heuer
44   * @author  Mark Schreiber
45   * @version $Revision: 997 $ $Date: 2011-03-04 11:11:21 -0600 (Fri, 04 Mar 2011) $
46   */
47  public final class HashWeightedMap<E>
48      implements WeightedMap<E>
49  {
50      /** Map of elements to weights. */
51      private final Map<E, Double> map;
52  
53      /** Total weight. */
54      private Double totalWeight = 0.0d;
55  
56      /** Source of randomness. */
57      private Random random = new Random();
58  
59      /** Map of elements to rank. */
60      private transient Map<E, Integer> rank;
61  
62      /** Dirty flag for calculating rank. */
63      private transient boolean dirty;
64  
65      /** Entry set view. */
66      private transient EntrySet entrySet;
67  
68      /** Key set view. */
69      private transient KeySet keySet;
70  
71      /** Values view. */
72      private transient Values values;
73  
74      /** Default initial capacity, <code>16</code>. */
75      private static final int DEFAULT_INITIAL_CAPACITY = 16;
76  
77      /** Default load factor, <code>0.75f</code>. */
78      private static final float DEFAULT_LOAD_FACTOR = 0.75f;
79  
80  
81      /**
82       * Create a new weighted map with the default initial capacity
83       * and load factor.
84       */
85      public HashWeightedMap()
86      {
87          map = new HashMap<E, Double>();
88          dirty = true;
89      }
90  
91      /**
92       * Create a new weighted map with the specified initial capacity
93       * and default load factor.
94       *
95       * @param initialCapacity initial capacity
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      * Create a new weighted map with the specified initial capacity
105      * and load factor.
106      *
107      * @param initialCapacity initial capacity
108      * @param loadFactor load factor
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      * Create a new weighted map with the elements and weights
118      * in the specified weighted map (copy constructor).
119      *
120      * @param weightedMap weighted map to copy, must not be null
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      * Set the source of randomness for this weighted map to
131      * <code>random</code>.
132      *
133      * @param random source of randomness, must not be null
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     /** {@inheritDoc} */
145     public void clear()
146     {
147         map.clear();
148         dirty = true;
149         totalWeight = 0.0d;
150     }
151 
152     /** {@inheritDoc} */
153     public int size()
154     {
155         return map.size();
156     }
157 
158     /** {@inheritDoc} */
159     public boolean isEmpty()
160     {
161         return map.isEmpty();
162     }
163 
164     /** {@inheritDoc} */
165     public boolean containsKey(final Object o)
166     {
167         return map.containsKey(o);
168     }
169 
170     /** {@inheritDoc} */
171     public boolean containsValue(final Object o)
172     {
173         return map.containsValue(o);
174     }
175 
176     /** {@inheritDoc} */
177     public Double get(final Object o)
178     {
179         return map.get(o);
180     }
181 
182     /** {@inheritDoc} */
183     public Double put(final E e, final Double w)
184     {
185         // TODO:  need to add this assertion to the API specification somehow
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
240     public Double weight(final E e)
241     {
242         return map.get(e);
243     }
244 
245     /** {@inheritDoc} */
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     /** {@inheritDoc} */
268     public Double totalWeight()
269     {
270         return totalWeight;
271     }
272 
273     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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      * Sort the elements in descending order according
308      * to their normalized weights and calculate rank.
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     /** {@inheritDoc} */
333     public Set<E> keySet()
334     {
335         if (keySet == null)
336         {
337             keySet = new KeySet();
338         }
339         return keySet;
340     }
341 
342     /** {@inheritDoc} */
343     public Collection<Double> values()
344     {
345         if (values == null)
346         {
347             values = new Values();
348         }
349         return values;
350     }
351 
352     /** {@inheritDoc} */
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      * Sort elements in descending order according to their
364      * normalized weights.
365      */
366     private transient final Comparator<E> byRankDescending = new Comparator<E>()
367         {
368             /** {@inheritDoc} */
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      * Key set wrapper.
380      */
381     private class KeySet
382         extends AbstractSet<E>
383     {
384 
385         /** {@inheritDoc} */
386         public int size()
387         {
388             return map.keySet().size();
389         }
390 
391         /** {@inheritDoc} */
392         public void clear()
393         {
394             HashWeightedMap.this.clear();
395         }
396 
397         /** {@inheritDoc} */
398         public Iterator<E> iterator()
399         {
400             return new KeySetIterator();
401         }
402     }
403 
404     /**
405      * Key set wrapper iterator.
406      */
407     private class KeySetIterator
408         implements Iterator<E>
409     {
410         /** Wrapped key set iterator. */
411         private Iterator<E> iterator;
412 
413         /** Last element. */
414         private E e;
415 
416 
417         /**
418          * Create a new key set wrapper iterator.
419          */
420         public KeySetIterator()
421         {
422             iterator = map.keySet().iterator();
423         }
424 
425 
426         /** {@inheritDoc} */
427         public boolean hasNext()
428         {
429             return iterator.hasNext();
430         }
431 
432         /** {@inheritDoc} */
433         public E next()
434         {
435             e = iterator.next();
436             return e;
437         }
438 
439         /** {@inheritDoc} */
440         public void remove()
441         {
442             Double w = weight(e);
443             iterator.remove();
444             dirty = true;
445             totalWeight -= w;
446         }
447     }
448 
449     /**
450      * Values wrapper.
451      */
452     private class Values
453         extends AbstractCollection<Double>
454     {
455 
456         /** {@inheritDoc} */
457         public int size()
458         {
459             return map.values().size();
460         }
461 
462         /** {@inheritDoc} */
463         public void clear()
464         {
465             HashWeightedMap.this.clear();
466         }
467 
468         /** {@inheritDoc} */
469         public Iterator<Double> iterator()
470         {
471             return new ValuesIterator();
472         }
473     }
474 
475     /**
476      * Values wrapper iterator.
477      */
478     private class ValuesIterator
479         implements Iterator<Double>
480     {
481         /** Wrapped values iterator. */
482         private Iterator<Double> iterator;
483 
484         /** Last weight. */
485         private Double w;
486 
487 
488         /**
489          * Create a new values wrapper iterator.
490          */
491         public ValuesIterator()
492         {
493             iterator = map.values().iterator();
494         }
495 
496 
497         /** {@inheritDoc} */
498         public boolean hasNext()
499         {
500             return iterator.hasNext();
501         }
502 
503         /** {@inheritDoc} */
504         public Double next()
505         {
506             w = iterator.next();
507             return w;
508         }
509 
510         /** {@inheritDoc} */
511         public void remove()
512         {
513             iterator.remove();
514             dirty = true;
515             totalWeight -= w;
516         }
517     }
518 
519     /**
520      * Entry set wrapper.
521      */
522     private class EntrySet
523         extends AbstractSet<Map.Entry<E, Double>>
524     {
525 
526         /** {@inheritDoc} */
527         public int size()
528         {
529             return map.entrySet().size();
530         }
531 
532         /** {@inheritDoc} */
533         public void clear()
534         {
535             HashWeightedMap.this.clear();
536         }
537 
538         /** {@inheritDoc} */
539         public Iterator<Map.Entry<E, Double>> iterator()
540         {
541             return new EntrySetIterator();
542         }
543     }
544 
545     /**
546      * Entry set wrapper iterator.
547      */
548     private class EntrySetIterator
549         implements Iterator<Map.Entry<E, Double>>
550     {
551         /** Wrapped key set iterator. */
552         private Iterator<Map.Entry<E, Double>> iterator;
553 
554         /** Last entry. */
555         private Map.Entry<E, Double> e;
556 
557 
558         /**
559          * Create a new key set wrapper iterator.
560          */
561         public EntrySetIterator()
562         {
563             iterator = map.entrySet().iterator();
564         }
565 
566 
567         /** {@inheritDoc} */
568         public boolean hasNext()
569         {
570             return iterator.hasNext();
571         }
572 
573         /** {@inheritDoc} */
574         public Map.Entry<E, Double> next()
575         {
576             e = iterator.next();
577             return new MapEntry(e);
578         }
579 
580         /** {@inheritDoc} */
581         public void remove()
582         {
583             iterator.remove();
584             dirty = true;
585             totalWeight -= e.getValue();
586         }
587     }
588 
589     /**
590      * Map entry wrapper.
591      */
592     private class MapEntry
593         implements Map.Entry<E, Double>
594     {
595         /** Wrapped map entry. */
596         private Map.Entry<E, Double> e;
597 
598 
599         /**
600          * Create a new wrapper for the specified map entry.
601          *
602          * @param e map entry to wrap
603          */
604         public MapEntry(final Map.Entry<E, Double> e)
605         {
606             this.e = e;
607         }
608 
609 
610         /** {@inheritDoc} */
611         public boolean equals(final Object o)
612         {
613             return e.equals(o);
614         }
615 
616         /** {@inheritDoc} */
617         public int hashCode()
618         {
619             return e.hashCode();
620         }
621 
622         /** {@inheritDoc} */
623         public E getKey()
624         {
625             return e.getKey();
626         }
627 
628         /** {@inheritDoc} */
629         public Double getValue()
630         {
631             return e.getValue();
632         }
633 
634         /** {@inheritDoc} */
635         public Double setValue(final Double w)
636         {
637             // TODO:  need to add this assertion to the API specification somehow
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 }