HashMap<String,Integer> 数据结构,但值被添加而不是被替换
HashMap<String,Integer> data structure, but values are added instead of replaced
我正在搜索一个几乎完全是 HashMap<String,Integer>
的数据结构,但是 HashMap 的问题是通过调用 putAll()
方法丢失了存储在键值对中的大部分数据在两个 HashMap 上,由于 java/util/HashMap.java
.
的 line 655
中 putVal()
的替换行为
这基本上就是我想要的改变:
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
-- e.value = value;
++ e.value = value + oldValue;
afterNodeAccess(e);
return oldValue;
}
是否有一个我忽略的现有数据结构会做这样的事情,或者我如何创建一个 class 基本上是一个更改的 HashMap?
我已经尝试编写一些代码,但没有按照我想要的方式工作...事实上,如果我在 @Override 上设置 put 方法,这样做并不重要,或者完全删除它 - 替换行为当然保持不变,因为 putAll()
使用 putVal()
我无法从外部到达/更改 - 或者我至少不知道如何...
/**
* doesn't work, putAll() uses putVal() that I can't reach
*/
public class SumHashMap<K> extends HashMap<K, Integer> {
private static final long serialVersionUID = 1L;
public Integer put(K key, Integer value) {
Integer oldValue = get(key);
if (oldValue == null)
return super.put(key, value);
return super.put(key, oldValue + value);
}
}
提前致谢
附加信息:
- 我想使用 putAll() 函数从自定义 HashMap 中减少流。
- 如果我有两个自定义 HashMap
{"key1" : 2, "key3" : 4}
和 {"key3" : 1}
a.putAll(b)
的结果应该是 {"key1" : 2, "key3" : 5}
我认为没有数据结构可以做到这一点。数据结构的目的是存储数据,而不是关联逻辑。 HashMap 可以为您存储键值对,但如果您需要一些更高级的或特定的与某些操作相关的逻辑,您需要自己添加它。
一种方法是将地图包装在具有此逻辑的 class 中。另一个可能是自己实现 Map 接口(也可以在内部使用 HashMap),但我不建议这样做,因为改变行为不是一个好主意。
提供添加功能的最小包装器:
public class AddingMap {
private final HashMap<String, Integer> map;
public AddingMap() {
map = new HashMap<>();
}
public void add(String key, Integer value) {
map.put(key, map.getOrDefault(key, 0) + value);
}
public Integer get(String key) {
return map.get(key);
}
}
编辑
答案应该不会写到一半...
的确,缺少 addAll() 方法:
public void addAll(Map<String, Integer> map) {
map.entrySet().forEach(e -> this.add(e.getKey(), e.getValue()));
}
我想这就是您要找的。我这样做是为了让钥匙可以是任何类型。如果需要,您可以删除键的泛型,只需扩展 HashMap.
import java.util.HashMap;
import java.util.Map;
public class AddingHashMap<K> extends HashMap<K, Integer> {
@Override
public Integer put(K key, Integer value) {
Integer existingValue = super.get(key);
if (existingValue == null) {
existingValue = value;
} else {
existingValue = existingValue.intValue() + value.intValue();
}
return super.put(key, existingValue);
}
@Override
public void putAll(Map<? extends K, ? extends Integer> m) {
m.entrySet().forEach(entry -> {
this.put(entry.getKey(), entry.getValue());
});
}
}
这是有效的:
public static void main(String[] argv) {
AddingHashMap<String> myAddingHashMap = new AddingHashMap<>();
myAddingHashMap.put("One", 1);
myAddingHashMap.put("Two", 2);
myAddingHashMap.put("One", 3);
myAddingHashMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " - " + entry.getValue()));
}
输出:
One - 4
Two - 2
稍后编辑:请记住,这不是线程安全的。
为此您不需要新的数据结构,您甚至不需要继承自 HashMap
的新 class。相反,使用 Map.merge
方法:
newMap.forEach((k, v) -> oldMap.merge(k, v, Integer::sum));
此代码使用 Map.forEach
遍历新地图的条目(您将在 putAll
中作为参数收到的地图)并使用 Map.merge
(连同 Integer::sum
) 将其条目合并到一个已经存在的地图中(我在这里命名为 oldMap
)。
我正在搜索一个几乎完全是 HashMap<String,Integer>
的数据结构,但是 HashMap 的问题是通过调用 putAll()
方法丢失了存储在键值对中的大部分数据在两个 HashMap 上,由于 java/util/HashMap.java
.
line 655
中 putVal()
的替换行为
这基本上就是我想要的改变:
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
-- e.value = value;
++ e.value = value + oldValue;
afterNodeAccess(e);
return oldValue;
}
是否有一个我忽略的现有数据结构会做这样的事情,或者我如何创建一个 class 基本上是一个更改的 HashMap?
我已经尝试编写一些代码,但没有按照我想要的方式工作...事实上,如果我在 @Override 上设置 put 方法,这样做并不重要,或者完全删除它 - 替换行为当然保持不变,因为 putAll()
使用 putVal()
我无法从外部到达/更改 - 或者我至少不知道如何...
/**
* doesn't work, putAll() uses putVal() that I can't reach
*/
public class SumHashMap<K> extends HashMap<K, Integer> {
private static final long serialVersionUID = 1L;
public Integer put(K key, Integer value) {
Integer oldValue = get(key);
if (oldValue == null)
return super.put(key, value);
return super.put(key, oldValue + value);
}
}
提前致谢
附加信息:
- 我想使用 putAll() 函数从自定义 HashMap 中减少流。
- 如果我有两个自定义 HashMap
{"key1" : 2, "key3" : 4}
和{"key3" : 1}
a.putAll(b)
的结果应该是{"key1" : 2, "key3" : 5}
我认为没有数据结构可以做到这一点。数据结构的目的是存储数据,而不是关联逻辑。 HashMap 可以为您存储键值对,但如果您需要一些更高级的或特定的与某些操作相关的逻辑,您需要自己添加它。
一种方法是将地图包装在具有此逻辑的 class 中。另一个可能是自己实现 Map 接口(也可以在内部使用 HashMap),但我不建议这样做,因为改变行为不是一个好主意。
提供添加功能的最小包装器:
public class AddingMap {
private final HashMap<String, Integer> map;
public AddingMap() {
map = new HashMap<>();
}
public void add(String key, Integer value) {
map.put(key, map.getOrDefault(key, 0) + value);
}
public Integer get(String key) {
return map.get(key);
}
}
编辑
答案应该不会写到一半...
的确,缺少 addAll() 方法:
public void addAll(Map<String, Integer> map) {
map.entrySet().forEach(e -> this.add(e.getKey(), e.getValue()));
}
我想这就是您要找的。我这样做是为了让钥匙可以是任何类型。如果需要,您可以删除键的泛型,只需扩展 HashMap
import java.util.HashMap;
import java.util.Map;
public class AddingHashMap<K> extends HashMap<K, Integer> {
@Override
public Integer put(K key, Integer value) {
Integer existingValue = super.get(key);
if (existingValue == null) {
existingValue = value;
} else {
existingValue = existingValue.intValue() + value.intValue();
}
return super.put(key, existingValue);
}
@Override
public void putAll(Map<? extends K, ? extends Integer> m) {
m.entrySet().forEach(entry -> {
this.put(entry.getKey(), entry.getValue());
});
}
}
这是有效的:
public static void main(String[] argv) {
AddingHashMap<String> myAddingHashMap = new AddingHashMap<>();
myAddingHashMap.put("One", 1);
myAddingHashMap.put("Two", 2);
myAddingHashMap.put("One", 3);
myAddingHashMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " - " + entry.getValue()));
}
输出:
One - 4
Two - 2
稍后编辑:请记住,这不是线程安全的。
为此您不需要新的数据结构,您甚至不需要继承自 HashMap
的新 class。相反,使用 Map.merge
方法:
newMap.forEach((k, v) -> oldMap.merge(k, v, Integer::sum));
此代码使用 Map.forEach
遍历新地图的条目(您将在 putAll
中作为参数收到的地图)并使用 Map.merge
(连同 Integer::sum
) 将其条目合并到一个已经存在的地图中(我在这里命名为 oldMap
)。