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

huangapple go评论102阅读模式
英文:

Java HashMap performing put() and get() in same line

问题

我正在使用一个HashMap<String, Integer>来跟踪特定字符串的出现次数。我以单线程的方式执行以下操作:

  1. HashMap&lt;String, Integer&gt; count = new HashMap&lt;&gt;();
  2. // List&lt;String&gt; words = ...;
  3. for (String word : words) {
  4. if (!count.containsKey(word)) {
  5. count.put(word, 0);
  6. }
  7. count.put(word, count.get(word) + 1);
  8. }

对于相同的单词,是否有可能计数增加超过1,因为我同时在同一个键上执行put和get操作?也就是说,假设word = "hello"。最初,count.get(word) = 1。当我执行count.put(word, count.get(word) + 1)时,如果我再执行count.get(word),我得到的不是2,而是3。

英文:

I am using a HashMap<String, Integer> to keep track of count of an occurrence of a specific string. I am performing this operation in a single-thread manner in the following way:

  1. HashMap&lt;String, Integer&gt; count = new HashMap&lt;&gt;();
  2. // List&lt;String≥ words = ...;
  3. for (String word : words) {
  4. if (!count.containsKey(word)) {
  5. count.put(word, 0);
  6. }
  7. count.put(word, count.get(word) + 1);
  8. }

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? i.e. 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.

答案1

得分: 2

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

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

Lambda表达式(prev, one) -&gt; prev + one实际上是一个接受两个int参数并返回它们之和的函数,因此可以用方法引用Integer::sum替代:

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

Map has methods compute and merge that would allow to implement shorter updates of the values for the keys:

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

Lambda expression (prev, one) -&gt; prev + one is actually a function of two int arguments returning their sum, therefore it may be replaced with a method reference Integer::sum:

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

答案2

得分: 2

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

你可以将缺失键的测试和初始化合并为一条语句:

  1. count.putIfAbsent(word, 0);

这会在返回值之后方便地执行:

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

然而,也有一种已经将这两个操作结合起来的方法:

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

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.

You can combine your missing key test and initialisation into a single statement:

  1. count.putIfAbsent(word, 0);

This conveniently returns the value afterwards, allowing:

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

However there is also a method that already combines those two operations:

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

答案3

得分: 1

以下是翻译好的部分:

  • "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." -> "不,'count 增加超过 1 是因为我同时在相同的键上执行 put 和 get 操作' 是不可能的,因为在单线程执行中不会同时发生两个操作。"
  • "Code count.put(word, count.get(word) + 1); will execute commands in following order:" -> "代码 count.put(word, count.get(word) + 1); 会按照以下顺序执行命令:"
  • "Integer value1 = count.get(word);" -> "Integer value1 = count.get(word);"
  • "int value2 = value1.intValue();" -> "int value2 = value1.intValue();"
  • "int value3 = value2 + 1;" -> "int value3 = value2 + 1;"
  • "Integer value4 = new Integer(value3);" -> "Integer value4 = new Integer(value3);"
  • "count.put(word, value4);" -> "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{" -> "private static class CounterHolder{"
  • "int value;" -> "int value;"
  • "Map<String, CounterHolder> count = new HashMap<>();" -> "Map<String, CounterHolder> count = new HashMap<>();"
  • "List<String> words = ..." -> "List words = ..."
  • "for (String word : words) {" -> "for (String word : words) {"
  • "CounterHolder holder;" -> "CounterHolder holder;"
  • "if (count.containsKey(word)) {" -> "if (count.containsKey(word)) {"
  • "holder = new CounterHolder();" -> "holder = new CounterHolder();"
  • "} else {" -> "} else {"
  • "holder = new CounterHolder();" -> "holder = new CounterHolder();"
  • "count.put(word, holder);" -> "count.put(word, holder);"
  • "++holder.value;" -> "++holder.value;"
英文:

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.

Code count.put(word, count.get(word) + 1); will execute commands in following order:

  1. Integer value1 = count.get(word);
  2. int value2 = value1.intValue();
  3. int value3 = value2 + 1;
  4. Integer value4 = new Integer(value3);
  5. 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:

  1. private static class CounterHolder{
  2. int value;
  3. }
  4. Map&lt;String, CounterHolder&gt; count = new HashMap&lt;&gt;();
  5. List&lt;String&gt; words = ...
  6. for (String word : words) {
  7. CounterHolder holder;
  8. if (count.containsKey(word)) {
  9. holder = new CounterHolder();
  10. } else {
  11. holder = new CounterHolder();
  12. count.put(word, holder);
  13. }
  14. ++holder.value;
  15. }

huangapple
  • 本文由 发表于 2020年10月24日 06:55:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/64508235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定