【发布时间】:2011-05-08 15:54:25
【问题描述】:
假设我们在 Java 中有一个 HashMap<String, Integer>。
对于我找到的每个存在的字符串,如何更新(增加)字符串键的整数值?
可以移除并重新进入该对,但开销将是一个问题。
另一种方法是只放入新的一对,然后替换旧的。
在后一种情况下,如果哈希码与我尝试插入的新密钥发生冲突会发生什么?哈希表的正确行为是为它分配一个不同的位置,或者在当前存储桶中列出它。
【问题讨论】:
假设我们在 Java 中有一个 HashMap<String, Integer>。
对于我找到的每个存在的字符串,如何更新(增加)字符串键的整数值?
可以移除并重新进入该对,但开销将是一个问题。
另一种方法是只放入新的一对,然后替换旧的。
在后一种情况下,如果哈希码与我尝试插入的新密钥发生冲突会发生什么?哈希表的正确行为是为它分配一个不同的位置,或者在当前存储桶中列出它。
【问题讨论】:
map.put(key, map.get(key) + 1);
应该没问题。它将更新现有映射的值。请注意,这使用自动装箱。在map.get(key) 的帮助下,我们获得了相应键的值,然后您可以根据您的要求进行更新。在这里,我将值更新为 1。
【讨论】:
getOrDefault 可以轻松避免这种情况,例如:map.put(key, count.getOrDefault(key, 0) + 1);
您可以使用computeIfPresent 方法并为其提供映射函数,该函数将被调用以根据现有值计算新值。
例如,
Map<String, Integer> words = new HashMap<>();
words.put("hello", 3);
words.put("world", 4);
words.computeIfPresent("hello", (k, v) -> v + 1);
System.out.println(words.get("hello"));
或者,您可以使用merge 方法,其中 1 是默认值,函数将现有值增加 1:
words.merge("hello", 1, Integer::sum);
此外,还有一堆其他有用的方法,如putIfAbsent、getOrDefault、forEach等。
【讨论】:
null(比如 words.put("hello", null);)时,它无法始终如一地工作,结果仍然是 null 而不是 1,正如我所期望的那样。
compute(),它也会处理null 值。
.merge 是我使用 Integer::sum 的解决方案。
简化的Java 8方式:
map.put(key, map.getOrDefault(key, 0) + 1);
这使用 HashMap 的方法来检索键的值,但如果无法检索键,则返回指定的默认值(在本例中为“0”)。
核心 Java 支持此功能:HashMap<K,V> getOrDefault(Object key, V defaultValue)
【讨论】:
hashmap.put(key, hashmap.get(key) + 1);
put 方法将替换现有键的值,如果不存在则创建它。
【讨论】:
nullPointer Exception。
null + 1 因为这会尝试将 null 拆箱一个整数来做增量。
将Integer 替换为AtomicInteger 并在其上调用incrementAndGet/getAndIncrement 方法之一。
另一种方法是将int 包装在您自己的具有increment() 方法的MutableInteger 类中,您只需要解决线程安全问题。
【讨论】:
MutableInteger 更好,因为AtomicInteger 使用volatile,它有开销。我会使用int[1] 而不是MutableInteger。
一线解决方案:
map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);
【讨论】:
@Matthew 的解决方案是最简单的,并且在大多数情况下都足够好。
如果您需要高性能,AtomicInteger 是更好的解决方案,@BalusC。
然而,一个更快的解决方案(假设线程安全不是问题)是使用TObjectIntHashMap,它提供了一个增量(键)方法并使用原语和比创建 AtomicIntegers 更少的对象。例如
TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
map.increment("aaa");
【讨论】:
您可以像下面这样递增,但您需要检查是否存在,以免引发 NullPointerException
if(!map.containsKey(key)) {
p.put(key,1);
}
else {
p.put(key, map.getKey()+1);
}
【讨论】:
散列是否存在(以 0 作为值)还是在第一次增量时“放入”到地图中?如果在第一个增量上“放置”,则代码应如下所示:
if (hashmap.containsKey(key)) {
hashmap.put(key, hashmap.get(key)+1);
} else {
hashmap.put(key,1);
}
【讨论】:
可能有点晚了,但这是我的两分钱。
如果您使用的是 Java 8,那么您可以使用 computeIfPresent 方法。如果指定键的值存在且非空,则它会尝试在给定键及其当前映射值的情况下计算新映射。
final Map<String,Integer> map1 = new HashMap<>();
map1.put("A",0);
map1.put("B",0);
map1.computeIfPresent("B",(k,v)->v+1); //[A=0, B=1]
我们还可以使用另一种方法putIfAbsent 来放置密钥。如果指定的键尚未与值关联(或映射为 null),则此方法将其与给定值关联并返回 null,否则返回当前值。
如果映射是跨线程共享的,那么我们可以使用ConcurrentHashMap 和AtomicInteger。来自文档:
AtomicInteger是一个可以自动更新的 int 值。一个 AtomicInteger 用于原子增量等应用程序中 计数器,并且不能用作整数的替代品。然而, 这个类确实扩展了 Number 以允许通过工具和统一访问 处理基于数字的类的实用程序。
我们可以如图所示使用它们:
final Map<String,AtomicInteger> map2 = new ConcurrentHashMap<>();
map2.putIfAbsent("A",new AtomicInteger(0));
map2.putIfAbsent("B",new AtomicInteger(0)); //[A=0, B=0]
map2.get("B").incrementAndGet(); //[A=0, B=1]
需要注意的一点是我们调用get 来获取键B 的值,然后调用incrementAndGet() 的值当然是AtomicInteger。我们可以优化它,因为 putIfAbsent 方法返回键的值(如果已经存在):
map2.putIfAbsent("B",new AtomicInteger(0)).incrementAndGet();//[A=0, B=2]
附带说明,如果我们计划使用AtomicLong,那么根据高争用下的文档,LongAdder 的预期吞吐量要高得多,但会消耗更多空间。还要检查这个question。
【讨论】:
没有 NullPointerException 的更简洁的解决方案是:
map.replace(key, map.get(key) + 1);
【讨论】:
由于声誉较低,我无法对一些答案发表评论,因此我将发布我应用的解决方案。
for(String key : someArray)
{
if(hashMap.containsKey(key)//will check if a particular key exist or not
{
hashMap.put(hashMap.get(key),value+1);// increment the value by 1 to an already existing key
}
else
{
hashMap.put(key,value);// make a new entry into the hashmap
}
}
【讨论】:
使用for 循环来增加索引:
for (int i =0; i<5; i++){
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("beer", 100);
int beer = map.get("beer")+i;
System.out.println("beer " + beer);
System.out ....
}
【讨论】:
这里有对这个问题的误导性答案,暗示如果键存在,Hashtable put 方法将替换现有值,这对于 Hashtable 不是这样,而是对于 HashMap。 HashMap 参见 Javadoc http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put%28K,%20V%29
【讨论】:
Integer i = map.get(key);
if(i == null)
i = (aValue)
map.put(key, i + 1);
或
Integer i = map.get(key);
map.put(key, i == null ? newValue : i + 1);
整数是原始数据类型http://cs.fit.edu/~ryan/java/language/java-data.html,所以你需要把它拿出来,做一些处理,然后把它放回去。如果你有一个不是 Primitive 数据类型的值,你只需要把它取出,处理它,不需要把它放回 hashmap 中。
【讨论】:
使用 Java8 内置函数“computeIfPresent”
例子:
public class ExampleToUpdateMapValue {
public static void main(String[] args) {
Map<String,String> bookAuthors = new TreeMap<>();
bookAuthors.put("Genesis","Moses");
bookAuthors.put("Joshua","Joshua");
bookAuthors.put("Judges","Samuel");
System.out.println("---------------------Before----------------------");
bookAuthors.entrySet().stream().forEach(System.out::println);
// To update the existing value using Java 8
bookAuthors.computeIfPresent("Judges", (k,v) -> v = "Samuel/Nathan/Gad");
System.out.println("---------------------After----------------------");
bookAuthors.entrySet().stream().forEach(System.out::println);
}
}
【讨论】:
试试:
HashMap hm=new HashMap<String ,Double >();
注意:
String->give the new value; //THIS IS THE KEY
else
Double->pass new value; //THIS IS THE VALUE
您可以更改哈希图中的键或值,但不能同时更改两者。
【讨论】: