英文:
Java HashMap performing put() and get() in same line
问题
我正在使用一个HashMap<String, Integer>来跟踪特定字符串的出现次数。我以单线程的方式执行以下操作:
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操作?也就是说,假设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:
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? 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
具有compute
和merge
方法,可实现对键的值的简短更新:
for (String word : words) {
count.compute(word, (w, prev) -> prev == null ? 1 : prev + 1);
}
for (String word : words) {
count.merge(word, 1, (prev, one) -> prev + one);
}
Lambda表达式(prev, one) -> prev + one
实际上是一个接受两个int
参数并返回它们之和的函数,因此可以用方法引用Integer::sum
替代:
for (String word : words) {
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:
for (String word : words) {
count.compute(word, (w, prev) -> prev == null ? 1 : prev + 1);
}
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
:
for (String word : words) {
count.merge(word, 1, Integer::sum);
}
答案2
得分: 2
直接回答你的问题: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);
英文:
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:
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);
答案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:
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;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论