【问题标题】:How to update a value, given a key in a hashmap?给定哈希图中的键,如何更新值?
【发布时间】:2011-05-08 15:54:25
【问题描述】:

假设我们在 Java 中有一个 HashMap<String, Integer>

对于我找到的每个存在的字符串,如何更新(增加)字符串键的整数值?

可以移除并重新进入该对,但开销将是一个问题。
另一种方法是只放入新的一对,然后替换旧的。

在后一种情况下,如果哈希码与我尝试插入的新密钥发生冲突会发生什么?哈希表的正确行为是为它分配一个不同的位置,或者在当前存储桶中列出它。

【问题讨论】:

    标签: java key hashmap


    【解决方案1】:
    map.put(key, map.get(key) + 1);
    

    应该没问题。它将更新现有映射的值。请注意,这使用自动装箱。在map.get(key) 的帮助下,我们获得了相应键的值,然后您可以根据您的要求进行更新。在这里,我将值更新为 1。

    【讨论】:

    • 事实上,这是最强大和可扩展的企业解决方案。
    • @Lavir,它不是一个糟糕的解决方案,但看不出它是最健壮和可扩展的。相反,原子整数更具可扩展性。
    • 这假设密钥存在,对吧?如果没有,我会收到 nullPointer 异常。
    • 在 Java 8 中,使用getOrDefault 可以轻松避免这种情况,例如:map.put(key, count.getOrDefault(key, 0) + 1);
    • @Martin..map.put(key, map.getOrDefault(key, 0) + 1)
    【解决方案2】:

    Java 8 方式:

    您可以使用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);
    

    此外,还有一堆其他有用的方法,如putIfAbsentgetOrDefaultforEach等。

    【讨论】:

    • 我刚刚测试了您的解决方案。第二个,一个有方法参考的,有效。第一个是 lambda 表达式,当您的地图的任何值是 null(比如 words.put("hello", null);)时,它无法始终如一地工作,结果仍然是 null 而不是 1,正如我所期望的那样。
    • 来自 Javadoc:“如果指定键的值存在且非空,则尝试计算新映射”。您可以改用compute(),它也会处理null 值。
    • 我想将我的值增加 1。.merge 是我使用 Integer::sum 的解决方案。
    【解决方案3】:

    简化的Java 8方式:

    map.put(key, map.getOrDefault(key, 0) + 1);
    

    这使用 HashMap 的方法来检索键的值,但如果无法检索键,则返回指定的默认值(在本例中为“0”)。

    核心 Java 支持此功能:HashMap<K,V> getOrDefault(Object key, V defaultValue)

    【讨论】:

    • 如果您使用的是 java 1.8,这是一个更好的选择
    【解决方案4】:
    hashmap.put(key, hashmap.get(key) + 1);
    

    put 方法将替换现有键的值,如果不存在则创建它。

    【讨论】:

    • 不,它不会创建,它会提供nullPointer Exception
    • 代码是给定问题的正确答案,但在完全相同的代码发布在接受的答案一年后发布。这个答案的不同之处在于说明 put 可以创建一个新条目,它可以,但在这个例子中不是。如果您将 hashmap.get(key) 用于不存在的键/值,您将获得 null 并且当您尝试递增时,正如@smttsp 所说,它将是 NPE。 -1
    • 这个答案是错误的。不存在的键的 NullPointerException
    • @smttp NullpointterException 仅当您没有初始化值时(因为您知道您不能增加 null )
    • 重复和不正确的解释... 如果不存在就会创建它 你不能这样做 null + 1 因为这会尝试将 null 拆箱一个整数来做增量。
    【解决方案5】:

    Integer 替换为AtomicInteger 并在其上调用incrementAndGet/getAndIncrement 方法之一。

    另一种方法是将int 包装在您自己的具有increment() 方法的MutableInteger 类中,您只需要解决线程安全问题。

    【讨论】:

    • AtomicInteger 是一个内置的可变整数。我严重怀疑编写自己的 MutableInteger 是一个更好的主意。
    • 自定义MutableInteger 更好,因为AtomicInteger 使用volatile,它有开销。我会使用int[1] 而不是MutableInteger
    • @Oliv: 不并发。
    • @BalusC 但是,易失性写入仍然更昂贵。它使缓存无效。如果没有区别,所有变量都会是 volatile。
    • @Oliv:问题明确提到了哈希码冲突,因此并发性对于 OP 很重要。
    【解决方案6】:

    一线解决方案:

    map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);
    

    【讨论】:

    • 这并没有为现有答案添加任何新内容,是吗?
    • 是的,确实如此。如果键不存在,它正确标记的答案将抛出 NullPointerException。这个解决方案可以正常工作。
    【解决方案7】:

    @Matthew 的解决方案是最简单的,并且在大多数情况下都足够好。

    如果您需要高性能,AtomicInteger 是更好的解决方案,@BalusC。

    然而,一个更快的解决方案(假设线程安全不是问题)是使用TObjectIntHashMap,它提供了一个增量(键)方法并使用原语和比创建 AtomicIntegers 更少的对象。例如

    TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
    map.increment("aaa");
    

    【讨论】:

      【解决方案8】:

      您可以像下面这样递增,但您需要检查是否存在,以免引发 NullPointerException

      if(!map.containsKey(key)) {
       p.put(key,1);
      }
      else {
       p.put(key, map.getKey()+1);
      }
      

      【讨论】:

        【解决方案9】:

        散列是否存在(以 0 作为值)还是在第一次增量时“放入”到地图中?如果在第一个增量上“放置”,则代码应如下所示:

        if (hashmap.containsKey(key)) {
            hashmap.put(key, hashmap.get(key)+1);
        } else { 
            hashmap.put(key,1);
        }
        

        【讨论】:

          【解决方案10】:

          可能有点晚了,但这是我的两分钱。

          如果您使用的是 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,否则返回当前值。

          如果映射是跨线程共享的,那么我们可以使用ConcurrentHashMapAtomicInteger。来自文档:

          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

          【讨论】:

            【解决方案11】:

            没有 NullPointerException 的更简洁的解决方案是:

            map.replace(key, map.get(key) + 1);
            

            【讨论】:

            • 如果key不存在那么map.get(key)会抛出NPE
            • 是的,这是真的
            【解决方案12】:

            由于声誉较低,我无法对一些答案发表评论,因此我将发布我应用的解决方案。

            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
               }
            }
            

            【讨论】:

              【解决方案13】:

              使用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 ....
              
              }
              

              【讨论】:

              • 这只会在每次迭代时覆盖 Map。有关正确方法,请参阅 Matthew 的答案。
              【解决方案14】:

              这里有对这个问题的误导性答案,暗示如果键存在,Hashtable put 方法将替换现有值,这对于 Hashtable 不是这样,而是对于 HashMap。 HashMap 参见 Javadoc http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put%28K,%20V%29

              【讨论】:

                【解决方案15】:
                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 中。

                【讨论】:

                • 感谢您提供此代码 sn-p,它可能会提供一些即时帮助。一个正确的解释would greatly improve 它的教育价值,通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有类似但不相同的问题的读者更有用。请edit您的答案添加解释,并说明适用的限制和假设。
                • 更正:整数类不是原始数据类型,而是原始类型 int 的包装类。此外,由于 Java8 之后的自动装箱,这里已经有一个公认的答案:stackoverflow.com/a/4158002/10993032
                【解决方案16】:

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

                【讨论】:

                  【解决方案17】:

                  试试:

                  HashMap hm=new HashMap<String ,Double >();
                  

                  注意:

                  String->give the new value; //THIS IS THE KEY
                  else
                  Double->pass new value; //THIS IS THE VALUE
                  

                  您可以更改哈希图中的键或值,但不能同时更改两者。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-05-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-05-19
                    相关资源
                    最近更新 更多