1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  package net.sf.statcvs.util;
24  
25  import java.io.Serializable;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.Comparator;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.TreeMap;
34  
35  /**
36   * Utility class for storing a map from <code>Object</code>s to
37   * <code>int</code>s.
38   * This class makes it easy to sort by key or value, and provides
39   * useful features like {@link #sum()}, {@link #max()}, and
40   * percent calculation.
41   * <p>
42   * The keys must be comparable, for example <code>String</code>s.
43   * <p>
44   * Behaviour for <code>null</code> keys is unspecified.
45   * 
46   * @author Richard Cyganiak
47   * @version $Id: IntegerMap.java,v 1.17 2009/08/20 17:44:05 benoitx Exp $
48   */
49  public class IntegerMap {
50  
51      private final Map map = new TreeMap();
52      private final Comparator comparator = new SortByValueComparator(map);
53      private int sum = 0;
54      private int max = 0;
55  
56      /**
57       * Puts a value into the map, overwriting any previous value
58       * for the same key.
59       * 
60       * @param key an <code>Object</code> which is used as key.
61       * @param value the <code>int</code> value to be stored at this key.
62       */
63      public void put(final Object key, final int value) {
64          max = Math.max(max, value);
65          sum -= get(key);
66          sum += value;
67          map.put(key, new Integer(value));
68      }
69  
70      /**
71       * Gets a value from the map. Returns the value which was
72       * stored in the map at the same key before. If no value was
73       * stored for this key, 0 will be returned.
74       * 
75       * @param key an <code>Object</code> which is used as key.
76       * @return the value for this key
77       */
78      public int get(final Object key) {
79          final Integer result = (Integer) map.get(key);
80          if (result == null) {
81              return 0;
82          }
83          return result.intValue();
84      }
85  
86      /**
87       * Same as {@link #get(Object)}, but returns an <code>Integer</code>,
88       * not an <code>int</code>.
89       * 
90       * @param key the key to get the value for
91       * @return the value wrapped in an <code>Integer</code> object
92       */
93      public Integer getInteger(final Object key) {
94          return (Integer) map.get(key);
95      }
96  
97      /**
98       * Gets the value stored at a key as a percentage of all values
99       * in the map.
100      * 
101      * @param key the key to get the value for
102      * @return the value as a percentage of the sum of all values
103      */
104     public double getPercent(final Object key) {
105         return (double) get(key) * 100 / sum;
106     }
107 
108     /**
109      * Gets the value stored at a key as a percentage of the maximum
110      * value in the map. For the maximum value, this will return
111      * 100.0. For a value half as large as the maximum value, this
112      * will return 50.0.
113      * 
114      * @param key the key to get the value for
115      * @return the value as a percentage of largest value in the map
116      */
117     public double getPercentOfMaximum(final Object key) {
118         return get(key) * 100 / (double) max;
119     }
120 
121     /**
122      * Adds an <code>int</code> to the value stored at a key.
123      * If no value was stored before at this key, the <code>int</code>
124      * will be stored there.
125      * 
126      * @param key the key to whose value <code>addValue</code> should be added
127      * @param addValue the <code>int</code> to be added
128      */
129     public void addInt(final Object key, final int addValue) {
130         put(key, addValue + get(key));
131     }
132 
133     /**
134      * Same as <code>addInt(key, 1)</code>
135      * 
136      * @param key the key whose value should be increased
137      */
138     public void inc(final Object key) {
139         addInt(key, 1);
140     }
141 
142     /**
143      * Same as <code>addInt(key, -1)</code>
144      * 
145      * @param key the key whose value should be decreased
146      */
147     public void dec(final Object key) {
148         addInt(key, -1);
149     }
150 
151     /**
152      * Deletes a value from the map. This is different from
153      * <code>put(key, 0)</code>. Removing will reduce
154      * the size of the map, putting 0 will not.
155      * 
156      * @param key the key that should be removed
157      */
158     public void remove(final Object key) {
159         sum -= get(key);
160         map.remove(key);
161     }
162 
163     /**
164      * Returns <code>true</code> if the map contains a value
165      * for this key.
166      * 
167      * @param key the key to check for
168      * @return <code>true</code> if the key is in the map
169      */
170     public boolean contains(final Object key) {
171         return map.containsKey(key);
172     }
173 
174     /**
175      * Returns the number of key-value pairs stored in the map.
176      * 
177      * @return the number of key-value pairs stored in the map
178      */
179     public int size() {
180         return map.size();
181     }
182 
183     /**
184      * Returns a set view of the keys. The set will be in
185      * ascending key order.
186      * 
187      * @return a <code>Set</code> view of all keys
188      */
189     public Set keySet() {
190         return map.keySet();
191     }
192 
193     /**
194      * Returns an iterator on the keys, sorted by key ascending.
195      * 
196      * @return an iterator on the keys
197      */
198     public Iterator iteratorSortedByKey() {
199         return map.keySet().iterator();
200     }
201 
202     /**
203      * Returns an iterator on the keys, sorted by values ascending.
204      * 
205      * @return an iterator on the keys
206      */
207     public Iterator iteratorSortedByValue() {
208         final List keys = new ArrayList(map.keySet());
209         Collections.sort(keys, comparator);
210         return keys.iterator();
211     }
212 
213     /**
214      * Returns an iterator on the keys, sorted by values descending.
215      * 
216      * @return an iterator on the keys
217      */
218     public Iterator iteratorSortedByValueReverse() {
219         final List keys = new ArrayList(map.keySet());
220         Collections.sort(keys, comparator);
221         Collections.reverse(keys);
222         return keys.iterator();
223     }
224 
225     /**
226      * Returns the sum of all values in the map.
227      * 
228      * @return the sum of all values in the map
229      */
230     public int sum() {
231         return sum;
232     }
233 
234     /**
235      * Returns the average of all values in the map.
236      * 
237      * @return the average of all values in the map
238      */
239     public double average() {
240         return (double) sum() / size();
241     }
242 
243     /**
244      * Returns the maximum value in the map.
245      * 
246      * @return the maximum value in the map.
247      */
248     public int max() {
249         return max;
250     }
251 
252     /**
253      * Private utility class for comparing of map entries by value.
254      */
255     private static class SortByValueComparator implements Comparator, Serializable {
256         private final Map mapToBeSorted;
257 
258         public SortByValueComparator(final Map map) {
259             this.mapToBeSorted = map;
260         }
261 
262         public int compare(final Object o1, final Object o2) {
263             final int i1 = ((Integer) this.mapToBeSorted.get(o1)).intValue();
264             final int i2 = ((Integer) this.mapToBeSorted.get(o2)).intValue();
265             if (i1 < i2) {
266                 return -1;
267             } else if (i1 > i2) {
268                 return 1;
269             }
270             return 0;
271         }
272     }
273 }