简体   繁体   English

Java HashMap 在同一行执行 put() 和 get()

[英]Java HashMap performing put() and get() in same line

I am using a HashMap<String, Integer> to keep track of count of an occurrence of a specific string.我正在使用 HashMap<String, Integer> 来跟踪特定字符串出现的次数。 I am performing this operation in a single-thread manner in the following way:我通过以下方式以单线程方式执行此操作:

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

Is it possible, for the same word, the count increases by more than 1 because I am performing a put and get on the same key at the same time?对于同一个单词,是否有可能因为我在同一时间执行 put 和 get 操作而导致计数增加超过 1? ie Let's say the word = "hello".即让我们说=“你好”这个词。 Initially, count.get(word) => 1. When I perform count.put(word, count.get(word) + 1), if I do count.get(word), instead of getting 2, I get 3.最初,count.get(word) => 1。当我执行count.put(word, count.get(word) + 1) 时,如果我执行count.get(word),我得到的不是2,而是3。

To answer your questions directly: no it is not possible for the statement count.put(word, count.get(word) + 1) to increment the value by more than 1. Although the two method calls are in the same statement they are performed sequentially: the get is performed first to find the second argument to pass to the put .直接回答您的问题:不,语句count.put(word, count.get(word) + 1)将值增加count.put(word, count.get(word) + 1)以上。虽然这两个方法调用在同一个语句中,但它们是顺序执行:首先执行get以找到要传递给put的第二个参数。

You can combine your missing key test and initialisation into a single statement:您可以将缺少的密钥测试和初始化组合到一个语句中:

count.putIfAbsent(word, 0);

This conveniently returns the value afterwards, allowing:这方便地在之后返回值,允许:

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

However there is also a method that already combines those two operations:然而,还有一种方法已经结合了这两个操作:

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

Map has methods compute and merge that would allow to implement shorter updates of the values for the keys: 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 expression (prev, one) -> prev + one is actually a function of two int arguments returning their sum, therefore it may be replaced with a method reference Integer::sum : Lambda 表达式(prev, one) -> prev + one实际上是两个int参数返回它们的总和的函数,因此它可以替换为方法引用Integer::sum

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

It absolutely safe to do it in a single thread.在单个线程中执行它绝对安全。 No, it's not possible that "count increases by more than 1 because I am performing a put and get on the same key at the same time" because two operations never can happen at the same time with single-threaded execution.不,“计数增加超过 1 是不可能的,因为我在同一时间执行放置和获取相同的键”,因为单线程执行永远不会同时发生两个操作。

Code count.put(word, count.get(word) + 1);代码count.put(word, count.get(word) + 1); will execute commands in following order:将按以下顺序执行命令:

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

By the way, your code will produce quite a lot of garbage and will be not very effective.顺便说一句,你的代码会产生相当多的垃圾并且不会很有效。

This way is more effective:这种方式更有效:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM