I got the following question from an interview.
I was given a character array like this:
char[] characters = {'u', 'a', 'u', 'i', 'o', 'f', 'u'};
I needed to get the distinct characters and counts of each character:
u = 3 a = 1 i = 1 o = 1 f = 1
So I answered in Java with the following code:
HashMap<Character, Integer> map = new HashMap<Character, Integer>(); int i = 1; for (char c : characters) { if (map.containsKey(c)) { int val = map.get(c); map.put(c, ++val); } else map.put(c, i); }
The interviewer was a solution architect. He asked me why I was using both containsKey()
and get()
methods here and noted that it was redundant to use both methods. What is his point? What was I doing wrong here? Will my code cause a performance issue, etc.?
Yes, it will cause major problems. One example is what could happen when adding a value to the hash map: this can cause a rehash of the table, and if that occurs while another thread is iterating over a collision list (a hash table "bucket"), that thread could erroneously fail to find a key that exists in the map.
HashMap. containsKey() method is used to check whether a particular key is being mapped into the HashMap or not. It takes the key element as a parameter and returns True if that element is mapped in the map.
get(Object) does explicitly warn about the subtle differences between Map. get(Object) and Map. containsKey(Object) : If this map permits null values, then a return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null .
get() method of HashMap class is used to retrieve or fetch the value mapped by a particular key mentioned in the parameter. It returns NULL when the map contains no such mapping for the key.
The architect means that get
and containsKey
have the same costs and may be accumulated into one check:
Integer val = map.get(c); if (val != null) { ... } else { ... }
But I wonder why the architect is only concerned about that, as there are more things to improve:
AtomicInteger
(or any other modifiable number class) instead of Integer
you can even merge the get with one of the putsSo from my point of view the best performance, when using a HashMap, would offer:
Map<Character, AtomicInteger> map = new HashMap<>(); for (Character c : characters) { AtomicInteger val = map.get(c); if (val != null) { val.incrementAndGet(); } else { map.put(c, new AtomicInteger(1)); } }
If the range of your characters is small (and known in advance), you could use an int array for counting. This would be the fastest of all possible solutions:
char firstCharacter = 'a'; char lastCharacter = 'z'; int[] frequency = new int[lastCharacter - firstCharacter + 1]; for (char c : characters) { frequency[c - firstCharacter]++; }
Your code is redundant, since both get and containsKey do pretty much the same work. Instead of calling containsKey you can check whether get returns a null value.
The code can be reduced to :
HashMap<Character, Integer> map = new HashMap<Character, Integer>(); for (char c : characters) { Integer val = map.get(c); if (val == null) val = 0; map.put(c,++val); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With