我正在编写用于文本处理的代码,如果我首先将字符串转换为整数,那么事情会更快很多。为了做到这一点,我创建了一个Dictionary类,每次看到一个新的字符串时,我给它一个索引,并保留两个映射,一个从string到int,一个从int到string,所以我可以很容易地查找两个方法。这是代码:
class Dictionary {
private Map<String, Integer> map;
private Map<Integer, String> reverse_map;
private int nextIndex;
public Dictionary() {
map = new HashMap<String, Integer>();
reverse_map = new HashMap<Integer, String>();
nextIndex = 1;
}
public int getIndex(String string) {
if (!map.containsKey(string)) {
map.put(string, nextIndex);
reverse_map.put(nextIndex, string);
nextIndex++;
}
return map.get(string);
}
public String getString(int index) {
// getIndex is always called first, so we don't need to check anything
return reverse_map.get(index);
}
}
在我的单线程代码中,这对我来说很好。但现在我想给这个多线程加速它,我不知道该怎么做。我想过使用ConcurrentHashMap,但我不确定putIfAbsent
是否会保证我不会使用索引两次。我不想使用Collections.synchronizedMap,因为这个字典在线程中经常被访问,因此我可能不会比使用单个线程好多了,因为它会阻塞每次读写。有没有办法使这项工作?
答案 0 :(得分:1)
您使用并发解决方案的问题是原子性。这些是我的想法:
private final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
private final ConcurrentMap<Integer, String> reverse_map = new ConcurrentHashMap<Integer, String>();
private final AtomicInteger nextIndex = new AtomicInteger(1);
public int getIndex(String string) {
Integer i = map.get(string);
if (i == null) {
final Integer newI = nextIndex.getAndIncrement();
i = map.putIfAbsent(string, newI);
if (i == null) {
reverse_map.put(newI, string);
return newI;
}
}
return i;
}
这有一个非常良性的失败模式:一些指数将被闲置。
请注意,我无条件地放入第二张地图,因为此时我知道我负责手头的字符串。
答案 1 :(得分:1)
最简单的方法是标记您的两种方法(getIndex
和getString
)synchronized
。看看你获得了什么样的加速。也许这就够了。
要使用ConcurrentHashMap
,您可以尝试这样做:
private AtomicInteger nextIndex;
public int getIndex(String string) {
Integer n = map.get(string);
if (n == null) {
int idx = nextIndex.getAndIncrement();
n = map.putIfAbsent(string, idx);
if (n != null) return n;
reverse_map.put(idx, string);
return idx;
}
return n;
}
如果两个线程同时插入相同的字符串,这可能偶尔会跳过索引,但它不会经常出现。