HashMap - 包含和获取方法不应一起使用

2022-09-01 09:18:51

我从一次采访中得到了以下问题。

我得到了一个这样的字符数组:

char[] characters = {'u', 'a', 'u', 'i', 'o', 'f', 'u'};

我需要获取每个字符的不同字符和计数:

u = 3
a = 1
i = 1
o = 1
f = 1

所以我用Java回答了以下代码:

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);
}

面试官是一名解决方案架构师。他问我为什么在这里同时使用这两种方法,并指出使用这两种方法是多余的。他的观点是什么?我在这里做错了什么?我的代码会导致性能问题等吗?containsKey()get()


答案 1

架构师的意思是,并且具有相同的成本,并且可以累积到一个检查中:getcontainsKey

Integer val = map.get(c);
if (val != null) {
  ...
} else {
  ...
}

但我想知道为什么架构师只关心这一点,因为还有更多需要改进的地方:

  • 通过接口引用对象(有效的 Java 第 2 版,项 52)
  • 从 Java 1.7 开始,您可以使用菱形运算符<>
  • 累积字符的自动装箱操作
  • 如果您使用(或任何其他可修改的数字类)而不是,您甚至可以将 get 与其中一个看跌期权合并AtomicIntegerInteger

因此,从我的角度来看,使用HashMap时,最佳性能将提供:

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));
    }
}

如果字符的范围很小(并且事先知道),则可以使用int数组进行计数。这将是所有可能的解决方案中最快的:

char firstCharacter = 'a';
char lastCharacter = 'z';
int[] frequency = new int[lastCharacter - firstCharacter + 1];
for (char c : characters) {
  frequency[c - firstCharacter]++;
}

答案 2

你的代码是多余的,因为 get 和 containsKey 都执行几乎相同的工作。您可以检查 get 是否返回空值,而不是调用 containsKey。

代码可以简化为:

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);
}