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 }