【问题标题】:Java HashMap performing put() and get() in same lineJava HashMap 在同一行执行 put() 和 get()
【发布时间】:2020-10-23 22:55:32
【问题描述】:

我正在使用 HashMap 来跟踪特定字符串的出现次数。我通过以下方式以单线程方式执行此操作:

   HashMap<String, Integer> count = new HashMap<>();
   // List<String≥ words = ...;
   for (String word : words) {
      if (!count.containsKey(word)) {
          count.put(word, 0);
      }
      count.put(word, count.get(word) + 1);
   }

是否有可能,对于同一个单词,计数增加超过 1 是因为我同时在同一个键上执行 put 和 get?即让我们说这个词=“你好”。最初,count.get(word) => 1。当我执行 count.put(word, count.get(word) + 1) 时,如果我执行 count.get(word),而不是得到 2,我得到 3。

【问题讨论】:

  • count.add(word, 0); 甚至不应该编译...
  • 除了你的问题,上面的代码可以重写成更易读的东西(对于熟悉Java 8中添加的流的人)到Map&lt;String, Long&gt; count = words.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting()));
  • 我的问题不是关于使用流。我编造了这个场景来专门询问在 HashMap 的同一行中 put() 和 get() 的行为
  • 顺便说一句,这不是一个可能的解释,请发布words 的内容
  • 这就是为什么我说“除了你的问题”:) 无论如何“我同时在同一个键上执行putget " 在单线程环境中,这些指令不会“同时”被调用,而是按顺序调用,特别是 count.put(word, count.get(word) + 1); put 方法 arguments 需要被评估(所以 count.get(word)+1 将生成结果首先)然后可以调用count.put(...);。 “如果我做 count.get(word),而不是得到 2,我得到 3。”不是这个问题的假设(单线程并使用正确的键从正确的地图打印值)。

标签: java hashmap


【解决方案1】:

直接回答您的问题:不,语句 count.put(word, count.get(word) + 1) 不可能将值增加超过 1。虽然两个方法调用在同一个语句中,但它们是按顺序执行的:执行 get首先找到要传递给put的第二个参数。

您可以将缺少的密钥测试和初始化合并到一个语句中:

count.putIfAbsent(word, 0);

这可以方便地在之后返回值,从而允许:

count.put(word, 1 + count.putIfAbsent(word, 0));

不过也有一种方法已经结合了这两种操作:

count.merge(word, 1, Integer::sum);

【讨论】:

    【解决方案2】:

    Map 有方法 computemerge 可以实现对键值的更短更新:

    1. compute
    for (String word : words) {
        count.compute(word, (w, prev) -> prev == null ? 1 : prev + 1);
    }
    
    1. merge
    for (String word : words) {
        count.merge(word, 1, (prev, one) -> prev + one);
    }
    

    Lambda 表达式 (prev, one) -&gt; prev + one 实际上是两个 int 参数返回其总和的函数,因此它可以替换为方法引用 Integer::sum

    for (String word : words) {
        count.merge(word, 1, Integer::sum);
    }
    

    【讨论】:

      【解决方案3】:

      在单个线程中执行此操作绝对安全。 不,“计数增加超过 1 是不可能的,因为我同时在同一个键上执行 put 和 get”,因为在单线程执行中两个操作永远不会同时发生。

      代码count.put(word, count.get(word) + 1);将按以下顺序执行命令:

      Integer value1 = count.get(word);
      int value2 = value1.intValue();
      int value3 = value2 + 1;
      Integer value4 = new Integer(value3);
      count.put(word, value4);
      

      顺便说一句,你的代码会产生相当多的垃圾,而且不会很有效。

      这种方式更有效:

      private static class CounterHolder{
          int value;
      }
      
      Map<String, CounterHolder> count = new HashMap<>();
      List<String> words = ...
      for (String word : words) {
          CounterHolder holder;
          if (count.containsKey(word)) {
              holder = new CounterHolder();
          } else {
              holder = new CounterHolder();
              count.put(word, holder);
          }
          ++holder.value;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-22
        相关资源
        最近更新 更多