Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
java中的并发双向映射_Java_Concurrency_Hashmap_Concurrenthashmap - Fatal编程技术网

java中的并发双向映射

java中的并发双向映射,java,concurrency,hashmap,concurrenthashmap,Java,Concurrency,Hashmap,Concurrenthashmap,我正在为文本处理编写代码,如果我先将字符串转换为整数,事情会变得更快。为此,我创建了一个Dictionary类,每次我看到一个新字符串时,我都给它一个索引,并保留两个映射,一个从字符串到int,一个从int到string,这样我就可以很容易地查找两种方式。代码如下: class Dictionary { private Map<String, Integer> map; private Map<Integer, String> reverse_map;

我正在为文本处理编写代码,如果我先将字符串转换为整数,事情会变得更快。为此,我创建了一个Dictionary类,每次我看到一个新字符串时,我都给它一个索引,并保留两个映射,一个从字符串到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);
    }
}
类字典{
私人地图;
私有地图;
私有int nextIndex;
公共词典(){
map=新的HashMap();
反向映射=新的HashMap();
nextIndex=1;
}
public int getIndex(字符串){
如果(!map.containsKey(字符串)){
map.put(字符串,nextIndex);
反向映射put(nextIndex,string);
nextIndex++;
}
返回map.get(字符串);
}
公共字符串getString(int索引){
//getIndex总是先被调用,所以我们不需要检查任何东西
返回反向映射get(索引);
}
}

在我的单线程代码中,这对我来说工作得很好。但是现在我想给它多个线程来加速它,我不知道怎么做。我曾想过使用ConcurrentHashMap,但我不确定
putIfAbsent
是否能保证我不会两次使用索引。我不想使用Collections.synchronizedMap,因为跨线程访问此词典的频率非常高,因此我可能不会比使用单个线程好多少,因为它会阻止每次读写。有什么方法可以使这项工作正常进行吗?

并发解决方案的问题是原子性。以下是我的想法:

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;
}
private final ConcurrentMap=new ConcurrentHashMap();
私有最终ConcurrentMap reverse_map=新ConcurrentHashMap();
私有最终AtomicInteger nextIndex=新的AtomicInteger(1);
public int getIndex(字符串){
整数i=map.get(字符串);
如果(i==null){
最终整数newI=nextIndex.getAndIncrement();
i=map.putIfAbsent(字符串,newI);
如果(i==null){
反向映射put(newI,string);
返回newI;
}
}
返回i;
}
这有一个非常良性的故障模式:一些索引将不使用


请注意,我无条件地放入了第二个映射,因为此时我知道我负责手头的字符串。

最简单的事情是只标记两个方法(
getIndex
getString
已同步。看看你能得到什么样的加速。也许足够了

要使用
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;
}

如果两个线程同时插入同一个字符串,这可能会偶尔跳过索引,但不会经常这样做。

CHM也使用锁,我想说,可以使用非锁读取器双向映射。永远不需要它,它也没有足够的兴趣去尝试。我认为唯一真正需要原子化的部分是第一次插入;第二个只是重复,如果我能保证在第一个上得到正确的结果,事情应该会好起来。我的问题是
nextIndex++
map.putIfAbsent
之间的相互作用。您可能可以做到,我只是对线程编程了解不够,无法确保它正确运行。@bestsss不只是在写操作上锁定CHM吗?@mattg我明白了,我错过了那部分
putIfAbsent
返回键下的上一个值,因此如果它返回一个非空值,您就知道其他线程已经在您前面处理该字符串了。在这种情况下,只需跳过第二步。我认为可能是这样,但必须再仔细考虑一下。当然,是的,新的CHM v8.x实际上更好,Cliff Click版本的并发哈希表完全是无锁的。看起来它可以工作了。我不知道
AtomicInteger
中的
getAndIncrement
,我担心可能会使用相同的索引引用两个不同的字符串。如果我总是
getAndIncrement
,我可能会像你说的那样跳过索引,但永远不会有重复的索引,这将非常糟糕。谢谢
n
几乎保证在您将其用作第二个映射中的键时为
null
putIfAbsent
返回键下的上一个值,您刚刚检查该值为
null
。对,如果
n==null
,请使用
idx
作为
反向映射中的键,否则实际上使用
n
@mattg(参见我的解决方案),否则不要使用任何东西--跳过该步骤。嗯,因此,您只需要insert/get,并且密钥是自生成的(即增量的?),如果是这样的话,算法甚至可以比上面的算法更快