View Javadoc
1   /*
2    * The basecode project
3    * 
4    * Copyright (c) 2008-2019 University of British Columbia
5    * 
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   *
18   */
19  package ubic.basecode.dataStructure;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  /**
31   * Convenience Map that has a count as the value for each key. Calling increment(K key) increases the count for
32   * <em>key</em>.
33   * <p>
34   * This replaces the idiom of map.containsKey(k) ? map.put(k, map.get(k) + 1) : map.put(k, 0);
35   * 
36   * @author luke
37   * 
38   * @param <K>
39   */
40  public class CountingMap<K> implements Map<K, Integer> {
41      private class AscendingCountComparator extends CountComparator {
42          /*
43           * (non-Javadoc)
44           * 
45           * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
46           */
47          @Override
48          public int compare( K key1, K key2 ) {
49              return map.get( key1 ).compareTo( map.get( key2 ) );
50          }
51      }
52  
53      private abstract class CountComparator implements Comparator<K> {
54      }
55  
56      private class DescendingCountComparator extends CountComparator {
57          /*
58           * (non-Javadoc)
59           * 
60           * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
61           */
62          @Override
63          public int compare( K key1, K key2 ) {
64              return map.get( key2 ).compareTo( map.get( key1 ) );
65          }
66      }
67  
68      private Map<K, Integer> map;
69  
70      /**
71       * Constructs a CountingMap backed by a simple HashMap.
72       */
73      public CountingMap() {
74          this( new HashMap<K, Integer>() );
75      }
76  
77      /**
78       * Constructs a CountingMap backed by the specified Map.
79       * 
80       * @param map the backing Map
81       */
82      public CountingMap( Map<K, Integer> map ) {
83          this.map = map;
84      }
85  
86      /*
87       * (non-Javadoc)
88       * 
89       * @see java.util.Map#clear()
90       */
91      @Override
92      public void clear() {
93          map.clear();
94      }
95  
96      /*
97       * (non-Javadoc)
98       * 
99       * @see java.util.Map#containsKey(java.lang.Object)
100      */
101     @Override
102     public boolean containsKey( Object key ) {
103         return map.containsKey( key );
104     }
105 
106     /*
107      * (non-Javadoc)
108      * 
109      * @see java.util.Map#containsValue(java.lang.Object)
110      */
111     @Override
112     public boolean containsValue( Object value ) {
113         return map.containsValue( value );
114     }
115 
116     /**
117      * Returns the count associated with the specified key, or zero if the key has never been incremented.
118      * 
119      * @param key the key whose associated count is to be returned
120      * @return the count associated with the specified key, or zero if the key has never been incremented
121      */
122     public int count( K key ) {
123         Integer i = map.get( key );
124         return i == null ? 0 : i;
125     }
126 
127     /*
128      * (non-Javadoc)
129      * 
130      * @see java.util.Map#entrySet()
131      */
132     @Override
133     public Set<java.util.Map.Entry<K, Integer>> entrySet() {
134         return map.entrySet();
135     }
136 
137     @Override
138     public boolean equals( Object o ) {
139         return map.equals( o );
140     }
141 
142     /*
143      * (non-Javadoc)
144      * 
145      * @see java.util.Map#get(java.lang.Object)
146      */
147     @Override
148     public Integer get( Object key ) {
149         return map.containsKey( key ) ? map.get( key ) : 0;
150     }
151 
152     @Override
153     public int hashCode() {
154         return map.hashCode();
155     }
156 
157     /**
158      * Increments the count associated with the specified key and returns the incremented count. If the key doesn't
159      * already exist in the map, it will be added.
160      * 
161      * @param key the key whose associated count is to be incremented
162      * @return the incremented value associated with the specified key
163      */
164     public int increment( K key ) {
165         Integer i = map.get( key );
166         if ( i == null ) i = 0;
167         map.put( key, ++i );
168         return i;
169     }
170 
171     /**
172      * Increments the count associated with the specified keys. If a key doesn't already exist in the map, it will be
173      * added.
174      * 
175      * @param keys the keys whose associated count is to be incremented
176      */
177     public void incrementAll( Collection<K> keys ) {
178         for ( K key : keys ) {
179             increment( key );
180         }
181     }
182 
183     /*
184      * (non-Javadoc)
185      * 
186      * @see java.util.Map#isEmpty()
187      */
188     @Override
189     public boolean isEmpty() {
190         return map.isEmpty();
191     }
192 
193     /*
194      * (non-Javadoc)
195      * 
196      * @see java.util.Map#keySet()
197      */
198     @Override
199     public Set<K> keySet() {
200         return map.keySet();
201     }
202 
203     /**
204      * @return
205      */
206     public int max() {
207         int r = 0;
208         for ( Integer i : map.values() ) {
209             if ( i > r ) {
210                 r = i;
211             }
212         }
213         return r;
214     }
215 
216     /*
217      * (non-Javadoc)
218      * 
219      * @see java.util.Map#put(java.lang.Object, java.lang.Object)
220      */
221     @Override
222     public Integer put( K key, Integer value ) {
223         return map.put( key, value );
224     }
225 
226     /*
227      * (non-Javadoc)
228      * 
229      * @see java.util.Map#putAll(java.util.Map)
230      */
231     @Override
232     public void putAll( Map<? extends K, ? extends Integer> t ) {
233         map.putAll( t );
234     }
235 
236     /*
237      * (non-Javadoc)
238      * 
239      * @see java.util.Map#remove(java.lang.Object)
240      */
241     @Override
242     public Integer remove( Object key ) {
243         return map.remove( key );
244     }
245 
246     /**
247      * Returns true if the specified key has ever been incremented, false otherwise.
248      * 
249      * @param key the key whose presence is to be tested
250      * @return true if the specified key has ever been incremented, false otherwise
251      */
252     public boolean seen( K key ) {
253         return map.containsKey( key );
254     }
255 
256     /*
257      * (non-Javadoc)
258      * 
259      * @see java.util.Map#size()
260      */
261     @Override
262     public int size() {
263         return map.size();
264     }
265 
266     /**
267      * Returns a list of the keys in this map, sorted by ascending count.
268      * 
269      * @return a list of the keys in this map, sorted by ascending count
270      */
271     public List<K> sortedKeyList() {
272         return sortedKeyList( false );
273     }
274 
275     /**
276      * Returns a list of the keys in this map, sorted as specified.
277      * 
278      * @param sortDescending true to sort by descending count, false to sort by ascending count
279      * @return a list of the keys in this map, sorted as specified.
280      */
281     public List<K> sortedKeyList( boolean sortDescending ) {
282         List<K> keys = new ArrayList<K>( keySet() );
283         Collections.sort( keys, sortDescending ? new DescendingCountComparator() : new AscendingCountComparator() );
284         return keys;
285     }
286 
287     @Override
288     public String toString() {
289         StringBuilder sb = new StringBuilder( "[" );
290         boolean first = true;
291         for ( K key : keySet() ) {
292             if ( !first ) sb.append( ", " );
293             sb.append( key.toString() + "=" + map.get( key ) );
294             first = false;
295         }
296         return sb.toString() + "]";
297     }
298 
299     /**
300      * Returns the sum of all counts in the map.
301      */
302     public int total() {
303         int summation = 0;
304         for ( int value : map.values() ) {
305             summation += value;
306         }
307         return summation;
308     }
309 
310     /*
311      * (non-Javadoc)
312      * 
313      * @see java.util.Map#values()
314      */
315     @Override
316     public Collection<Integer> values() {
317         return map.values();
318     }
319 
320 }