这个问题可能被认为太基础了,但是在论坛上经常被问到。 在本文中,我将讨论一种仅在Map ONCE中搜索键的方法。
让我们首先来看一个例子。 假设我正在使用Map创建一个字符串频率列表,其中每个键是一个正在计数的String ,值是一个Integer ,每次添加一个String都会递增。 实现它的一种直接方法是
int count = map.containsKey(string) ? map.get(string) : 0;
map.put(string, count + 1);
这段代码运行很慢,因为它在地图上包含三个潜在的昂贵操作,即containsKey() , get()和[put()](http://docs.oracle.com/javase/7/docs/ api / java / util / Map.html#put(K,V)) 。 每个都需要在地图中搜索关键字。 现在,让我们重构代码以获得更好的性能。
整数与MutableInteger与AtomicInteger
我们必须调用三个昂贵的操作的重要原因之一是使用Integer进行计数。 在Java中, Integer是不可变的 。 它阻止我们在构造后修改整数值。 因此,要增加一个计数器,我们必须首先从映射中获取整数,然后通过添加一个整数来创建另一个新整数,然后将其放回映射中。
为了使计数器可变,有几种方法。 一种是简单地创建自己的MutableInteger ,就像我在下面显示的那样。
public class MutableInteger {private int val;public MutableInteger(int val) {this.val = val;}public int get() {return val;}public void set(int val) {this.val = val;}
}
另一种方法可能是在Java中使用AtomicInteger ,该方法用于原子增量计数器等应用程序中。 但是AtomicInteger的主要选择是如果您希望通过对整数进行操作来实现线程安全。 因此,它不能用作Integer的替代。 基于此,如果您的项目不是线程安全性的重要考虑因素,则我不建议您使用AtomicInteger 。
仅搜索一次密钥
使用MutableInteger之后 ,我们可以将上面的代码更改为
if (map.containsKey(string)) {MutableInteger count = map.get(string);count.set(count.get() + 1);
} else {map.put(string, new MutableInteger(1));
}
要么
MutableInteger count = map.get(string);
if (count != null) {count.set(count.get() + 1);
} else {map.put(string, new MutableInteger(1));
}
在最坏的情况下,如果以前没有看到过密钥,则代码将搜索密钥两次:一次用于检索,一次用于设置。 它比上一个要好得多。 但是我们不应该立即满足并停止。 如果您在Java文档中选中了[Map.put()](http://docs.oracle.com/javase/7/docs/api/java/util/Map.html#put(K,V))方法,您会发现此方法将返回the previous value associated with key
。 这意味着我们可以将检索和设置合并为一个。 但是,您可能想知道:如果不首先检索计数器,如何设置新计数器? 现在,我们终于可以触摸本文中最棘手的部分:我们可以简化放置零频率计数器的工作!
public int incrementCount(K key, int count) {MutableInteger tmpCount = new MutableInteger(0);MutableInteger oldCount = map.put(key, tmpCount);if (oldCount != null) {count += oldCount.get();}tmpCount.set(count);return count;}
另一个柜台
看起来将所有必要的操作放入一个类中将对将来的使用有所帮助。 因此,我创建了一个称为Counter的类并将其公开。 计数器定义一个集合,该集合对对象出现在集合中的次数进行计数。 假设您有一个包含{a, a, b, c}
的Counter。 在“ a”上调用getCount()将返回2,而在keySet()上调用将返回{a, b, c}
。 此类的工作方式类似于Map ,但是具有不同的方法来轻松获取/设置/增加对象的计数以及使用该计数来计算各种函数。 Counter构造函数和addAll()方法可用于复制另一个Counter的内容。 根据IntCounter和AbstractMapBag修改Counter类。
Counter上的一些突出操作包括
- gainCount()和decrementCount() :将给定键的给定计数加/减到当前计数中。 如果之前没有看到该键,则假定它的计数为0,因此增量方法会将其计数设置为给定的数量。 减量会将其计数设置为-1。
- getCount() :返回给定键的当前计数,如果以前没有看到过,则返回0。
- keysAt() , keysAbove()和keysBelow() :返回其计数在给定阈值之上,之下或之下的一组键。 该集合可能包含0个元素,但不会为null。
- argmin ()和argmax() :找到并返回此Counter中具有最小/最大计数的密钥。 如果有几个最小/最大计数,则返回随机值。 如果此Counter为空,则返回null。
翻译自: https://www.javacodegeeks.com/2013/10/most-efficient-way-to-increment-a-map-value-in-java-only-search-the-key-once.html