Java 使用putIfAbsent重叠ConcurrentHashMap放置
在哈希表中插入数据似乎有问题。我创建了大约8个线程,在每个线程中我执行以下代码。每个线程接收一个char[]数组。每个线程的任务是标记这个数组(查找空格)。一旦找到令牌,如果它不存在,我需要将其添加到哈希表中。如果它确实存在,那么我需要将1添加到该令牌(密钥)的当前值 您可能会问的问题: 为什么不将字符[]转换为字符串? 我尝试过这个,由于字符串是不可变的,我最终耗尽了内存(我正在处理一个10g文件),或者我花了太长时间收集垃圾。使用字符[],我可以重用相同的变量,而不会占用内存中的额外空间 什么问题? 处理完整个文件后,我运行以下代码:Java 使用putIfAbsent重叠ConcurrentHashMap放置,java,multithreading,hashmap,wrapper,concurrenthashmap,Java,Multithreading,Hashmap,Wrapper,Concurrenthashmap,在哈希表中插入数据似乎有问题。我创建了大约8个线程,在每个线程中我执行以下代码。每个线程接收一个char[]数组。每个线程的任务是标记这个数组(查找空格)。一旦找到令牌,如果它不存在,我需要将其添加到哈希表中。如果它确实存在,那么我需要将1添加到该令牌(密钥)的当前值 您可能会问的问题: 为什么不将字符[]转换为字符串? 我尝试过这个,由于字符串是不可变的,我最终耗尽了内存(我正在处理一个10g文件),或者我花了太长时间收集垃圾。使用字符[],我可以重用相同的变量,而不会占用内存中的额外空间 什
for (Entry<Character [], Integer> e : wordCountMap.entrySet()) {
System.out.println(Arrays.toString(e.getKey()) + " = " + e.getValue());
}
被执行。因此,我的问题是,如何传递值?现在它实际上非常合理,因为最终大约有320个键/值对-8个线程,40个循环(每个线程每次迭代获得250/8 MB)。当使用数组作为键时,使用的是数组本身的引用,而不是内容。因此,更改内容不会在地图中创建更多条目,只会不断更新相同的值。考虑简单的程序:
public static void main(String[] args) throws Exception {
Character[] charArray = new Character[8];
charArray[1] = 'A';
Set<Character[]> set = new HashSet<Character[]>();
set.add(charArray);
charArray[1] = 'B';
System.out.println(set.contains(charArray));
}
你必须把它放在某个地方!如果内存太大,则需要分配更多内存或使用某种外部存储。也许在MD5上键入字符串的映射,并在MD5->原始字符串的磁盘上保留一个NoSQL数据库,以便您可以稍后将其取回?在您的代码中,您在运行时删除了数据,但希望它最终仍然存在 我认为,您需要定义自己的类来表示8字符数组(*),而不是使用
字符[]
作为映射键。您需要在该类中重新定义equals()
和hashCode()
;定义equals()
以便在所有8个字符都相同时返回true
,并将hashCode()
定义为依赖于这8个字符的某个值。不能为数组重新定义equals()
或hashCode()
;这就是为什么您需要定义自己的类。该类将在内部使用char[]
或Character[]
该类还应具有某种类型的copy
或clone
方法,或复制构造函数,以便您可以创建数据(8个字符)与现有对象相同的新对象
现在,不是这样:
check = wordCountMap.putIfAbsent(charArray, 1);
if (check != null) {
wordCountMap.put(charArray, wordCountMap.get(charArray) + 1);
}
将新密钥放入地图时,您需要确保使用副本。如上所述,使用putIfAbsent
将在映射中放置对局部变量的引用,这是错误的,因为局部变量可能会更改。这也是错误的:
check = wordCountMap.putIfAbsent(new CharArray(charArray), 1);
其中,newchararray(CharArray)
复制现有数组——这就是我所说的“复制构造函数”。(我假设CharArray
是您为新类指定的名称。)这是错误的,因为在不需要新对象的情况下,您将创建新对象,这是您试图避免的。所以可能是
Integer existing = wordCountMap.get(charArray);
if (existing == null) {
wordCountMap.put(new CharArray(charArray), 1);
} else {
wordCountMap.put(charArray, existing + 1);
}
这应该只在需要时创建一个新的CharArray
,并且它不会在地图中放置对您计划不断更改的CharArray
的引用。您可能需要在上面添加一些锁定,以防止出现竞争条件
(*)在再次查看您的帖子后,我不确定8字符数组是否是您真正想要的,但您确实在代码中说了
新字符[8]
。不过,这种技术应该适用于任何缓冲区大小。您可以设置类,使可变的实例具有更大的缓冲区,并且您放入哈希映射中的实例只保留所需的字符数。如果不同步get()和put()操作,我认为这是不可能实现的
按照
检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。检索反映了最近完成的更新操作在开始时的结果
这意味着如果两个线程同时遇到相同的计数器,get()将返回相同的值(比如2),并且两个线程都将插入2+1=3。因此,令牌的数量将被低估-即3而不是4
为了保持一致,您需要在执行get()操作之前进行同步,这将大大降低多线程的好处
如果你想这样做,你可以这样做:
class Key {
char[] buffer = new char[8];
Key copy() {
Key copy = new Key();
for ( int i =0; i < 8; i++) {
copy.buffer[i] = this.buffer[i];
}
}
public int hashCode() {
return Arrays.hashCode(buffer);
}
public boolean equals(Object obj) {
if ( obj instanceof Key) {
return Arrays.equals(((Key) obj).buffer, this.buffer);
}
return false;
}
}
//YOur code modified:
Key checker = new Key();
for (i = 0; i < newbyte.length; i++) { //newbyte is a char[] from main
if (newbyte[i] != ' ') {
checker.buffer[counter] = newbyte[i];
counter++;
}
else {
synchronized (wordCountMap) {
Integer value = workCountMap.get(checker);
if ( value == null ) {
workCountMap.put(checker.copy(), 1);
} else {
wordCountMap.put(checker.copy(), value + 1);
}
}
for (j = 0; j < counter; j++) {
checker.buffer[j] = null;
}//Null out the array
}
类密钥{
char[]buffer=新字符[8];
密钥副本(){
密钥副本=新密钥();
对于(int i=0;i<8;i++){
copy.buffer[i]=这个.buffer[i];
}
}
公共int hashCode(){
返回数组。哈希代码(缓冲区);
}
公共布尔等于(对象obj){
if(obj键实例){
返回array.equals(((Key)obj.buffer,this.buffer);
}
返回false;
}
}
//您的代码已修改:
密钥检查器=新密钥();
对于(i=0;i
这将解决您的内存问题,因为您需要执行new()
check = wordCountMap.putIfAbsent(new CharArray(charArray), 1);
Integer existing = wordCountMap.get(charArray);
if (existing == null) {
wordCountMap.put(new CharArray(charArray), 1);
} else {
wordCountMap.put(charArray, existing + 1);
}
class Key {
char[] buffer = new char[8];
Key copy() {
Key copy = new Key();
for ( int i =0; i < 8; i++) {
copy.buffer[i] = this.buffer[i];
}
}
public int hashCode() {
return Arrays.hashCode(buffer);
}
public boolean equals(Object obj) {
if ( obj instanceof Key) {
return Arrays.equals(((Key) obj).buffer, this.buffer);
}
return false;
}
}
//YOur code modified:
Key checker = new Key();
for (i = 0; i < newbyte.length; i++) { //newbyte is a char[] from main
if (newbyte[i] != ' ') {
checker.buffer[counter] = newbyte[i];
counter++;
}
else {
synchronized (wordCountMap) {
Integer value = workCountMap.get(checker);
if ( value == null ) {
workCountMap.put(checker.copy(), 1);
} else {
wordCountMap.put(checker.copy(), value + 1);
}
}
for (j = 0; j < counter; j++) {
checker.buffer[j] = null;
}//Null out the array
}
check = wordCountMap.putIfAbsent(charArray, 1);
if (check != null) {
wordCountMap.put(charArray, wordCountMap.get(charArray) + 1);
}
AtomicInteger check = wordCountMap.putIfAbsent(key.copy(), new AtomicInteger(1));
if (check != null) {
check.incrementAndGet();
}