1. Overview
1.概述
This tutorial will go through the different approaches for updating the value associated with a given key in a HashMap. First, we’ll look at some common solutions using only those features that were available before Java 8. Then, we’ll look at some additional solutions available in Java 8 and above.
本教程将介绍更新HashMap中与指定键相关的值的不同方法。首先,我们将看看一些常见的解决方案,这些解决方案只使用 Java 8 之前的那些功能。然后,我们将看看在Java 8及以上版本中可用的一些额外解决方案。
2. Initializing Our Example HashMap
2.初始化我们的例子HashMap
To show how to update the values in a HashMap, we have to create and populate one first. So, we’ll create a map with fruits as keys and their prices as the values:
为了展示如何更新HashMap中的值,我们必须先创建并填充一个。因此,我们将创建一个以水果为键、以其价格为值的地图。
Map<String, Double> priceMap = new HashMap<>();
priceMap.put("apple", 2.45);
priceMap.put("grapes", 1.22);
We’ll be using this HashMap throughout our example. Now, we’re ready to get familiar with the methods for updating the value associated with a HashMap key.
我们将在整个示例中使用这个HashMap。现在,我们准备熟悉一下更新与HashMap键相关的值的方法。
3. Before Java 8
3.在Java 8之前
Let’s start with the methods that were available before Java 8.
让我们从Java 8之前的方法开始。
3.1. The put Method
3.1.put方法
The put method either updates the value or adds a new entry. If it is used with a key that already exists, then the put method will update the associated value. Otherwise, it will add a new (key, value) pair.
put方法要么更新值,要么添加一个新条目。如果它被用于一个已经存在的键,那么put方法将更新相关的值。否则,它将添加一个新的(key,value)对。
Let’s test the behavior of this method with two quick examples:
让我们用两个快速的例子来测试这个方法的行为。
@Test
public void givenFruitMap_whenPuttingAList_thenHashMapUpdatesAndInsertsValues() {
Double newValue = 2.11;
fruitMap.put("apple", newValue);
fruitMap.put("orange", newValue);
Assertions.assertEquals(newValue, fruitMap.get("apple"));
Assertions.assertTrue(fruitMap.containsKey("orange"));
Assertions.assertEquals(newValue, fruitMap.get("orange"));
}
The key apple is already on the map. Therefore, the first assertion will pass.
键apple已经在地图上。因此,第一个断言将通过。
Since orange is not present in the map, the put method will add it. Hence, the other two assertions will pass as well.
由于orange不存在于地图中,put方法将添加它。因此,其他两个断言也将通过。
3.2. The Combination of containsKey and put Methods
3.2.containsKey和put方法的组合
The combination of containsKey and put methods is another way to update the value of a key in HashMap. This option checks if the map already contains a key. In such a case, we can update the value using the put method. Otherwise, we can either add an entry to the map or do nothing.
containsKey和put方法的组合是更新HashMap中一个键的值的另一种方法。该选项检查地图是否已经包含了一个键。在这种情况下,我们可以使用put方法来更新值.否则,我们可以向地图添加一个条目或者什么都不做。
In our case, we’ll inspect this approach with a simple test:
在我们的案例中,我们将用一个简单的测试来检查这种方法。
@Test
public void givenFruitMap_whenKeyExists_thenValuesUpdated() {
double newValue = 2.31;
if (fruitMap.containsKey("apple")) {
fruitMap.put("apple", newValue);
}
Assertions.assertEquals(Double.valueOf(newValue), fruitMap.get("apple"));
}
Since apple is on the map, the containsKey method will return true. Therefore, the call to the put method will be executed, and the value will be updated.
由于apple在地图上,containsKey方法将返回true。因此,对put方法的调用将被执行,并且值将被更新。
4. Java 8 and Above
4.Java 8及以上版本
Since Java 8, many new methods are available that facilitate the process of updating the value of a key in the HashMap. So, let’s get to know them.
从Java 8开始,有许多新的方法可以促进更新HashMap中某个键的值的过程。
4.1. The replace Methods
4.1.replace方法
Two overloaded replace methods have been available in the Map interface since version 8. Let’s look at the method signatures:
自版本8以来,Map接口中就有两个重载的replace方法。让我们来看看这些方法的签名。
public V replace(K key, V value);
public boolean replace(K key, V oldValue, V newValue);
The first replace method only takes a key and a new value. It also returns the old value.
第一个replace方法只接受一个键和一个新值。它也会返回旧值。。
Let’s see how the method works:
让我们看看这个方法是如何运作的。
@Test
public void givenFruitMap_whenReplacingOldValue_thenNewValueSet() {
double newPrice = 3.22;
Double applePrice = fruitMap.get("apple");
Double oldValue = fruitMap.replace("apple", newPrice);
Assertions.assertNotNull(oldValue);
Assertions.assertEquals(oldValue, applePrice);
Assertions.assertEquals(Double.valueOf(newPrice), fruitMap.get("apple"));
}
The value of the key apple will be updated to a new price with the replace method. Therefore, the second and the third assertions will pass.
键apple的值将通过replace方法被更新为新的价格。因此,第二个和第三个断言将通过。
However, the first assertion is interesting. What if there was no key apple in our HashMap? If we try to update the value of a non-existing key, null will be returned. Taking that into account, another question arises: What if there was a key with a null value? We cannot know whether that value returned from the replace method was indeed the value of the provided key or if we’ve tried to update the value of a non-existing key.
然而,第一个断言很有意思。如果我们的HashMap中没有键apple怎么办?如果我们试图更新一个不存在的键的值,null将被返回。考虑到这一点,另一个问题出现了。如果有一个键的值是null呢?我们无法知道从replace方法返回的值是否真的是所提供的键的值,或者我们是否试图更新一个不存在的键的值。
So, to avoid misunderstanding, we can use the second replace method. It takes three arguments:
所以,为了避免误解,我们可以使用第二个replace方法。它需要三个参数。
- a key
- the current value associated with the key
- the new value to associate with the key
It will update the value of a key to a new value on one condition: If the second argument is the current value, the key value will be updated to a new value. The method returns true for a successful update. Otherwise, false is returned.
它将在一个条件下把一个键的值更新为一个新的值:如果第二个参数是当前值,键的值将被更新为一个新的值。如果更新成功,该方法返回true。否则,将返回false。
So, let’s implement some tests to check the second replace method:
因此,让我们实现一些测试来检查第二个replace方法。
@Test
public void givenFruitMap_whenReplacingWithRealOldValue_thenNewValueSet() {
double newPrice = 3.22;
Double applePrice = fruitMap.get("apple");
boolean isUpdated = fruitMap.replace("apple", applePrice, newPrice);
Assertions.assertTrue(isUpdated);
}
@Test
public void givenFruitMap_whenReplacingWithWrongOldValue_thenNewValueNotSet() {
double newPrice = 3.22;
boolean isUpdated = fruitMap.replace("apple", Double.valueOf(0), newPrice);
Assertions.assertFalse(isUpdated);
}
Since the first test calls the replace method with the current value of the key, that value will be replaced.
由于第一个测试用键的当前值调用replace 方法,该值将被替换。
On the other hand, the second test is not invoked with the current value. Thus, false is returned.
另一方面,第二个测试没有用当前值调用。因此,false被返回。
4.2. The Combination of getOrDefault and put Methods
4.2.getOrDefault和put M方法的组合
The getOrDefault method is a perfect choice if we don’t have an entry for the provided key. In that case, we set the default value for a non-existing key. Then, the entry is added to the map. With this approach, we can easily escape the NullPointerException.
getOrDefault方法是一个完美的选择如果我们没有提供的键的条目。在这种情况下,我们为一个不存在的键设置默认值。然后,该条目被添加到地图中。通过这种方法,我们可以轻松地摆脱NullPointerException。
Let’s try this combination with a key that is not originally on the map:
让我们用一个原本不在地图上的钥匙试试这个组合。
@Test
public void givenFruitMap_whenGetOrDefaultUsedWithPut_thenNewEntriesAdded() {
fruitMap.put("plum", fruitMap.getOrDefault("plum", 2.41));
Assertions.assertTrue(fruitMap.containsKey("plum"));
Assertions.assertEquals(Double.valueOf(2.41), fruitMap.get("plum"));
}
Since there is no such key, the getOrDefault method will return the default value. Then, the put method will add a new (key, value) pair. Therefore, all assertions will pass.
由于没有这样的键,getOrDefault方法将返回默认值。然后,put方法将添加一个新的(键,值)对。因此,所有的断言都将通过。
4.3. The putIfAbsent Method
4.3.putIfAbsent方法
The putIfAbsent method does the same as the previous combination of the getOrDefault and put methods.
putIfAbsent方法的作用与之前的getOrDefault和put方法的组合相同。
If there is no pair in the HashMap with the provided key, the putIfAbsent method will add the pair. However, if there is such a pair, the putIfAbsent method won’t change the map.
如果在HashMap中没有提供键的配对,putIfAbsent方法将添加该配对。然而,如果有这样的一对,putIfAbsent方法将不会改变地图。
But, there is an exception: If the existing pair has a null value, then the pair will be updated to a new value.
但是,有一个例外。如果现有的配对有一个null值,那么该配对将被更新为一个新值。。
Let’s implement the test for the putIfAbsent method. We’ll test the behavior with two examples:
让我们来实现对putIfAbsent方法的测试。我们将用两个例子来测试该行为。
@Test
public void givenFruitMap_whenPutIfAbsentUsed_thenNewEntriesAdded() {
double newValue = 1.78;
fruitMap.putIfAbsent("apple", newValue);
fruitMap.putIfAbsent("pear", newValue);
Assertions.assertTrue(fruitMap.containsKey("pear"));
Assertions.assertNotEquals(Double.valueOf(newValue), fruitMap.get("apple"));
Assertions.assertEquals(Double.valueOf(newValue), fruitMap.get("pear"));
}
A key apple is present on the map. The putIfAbsent method won’t change its current value.
地图上有一个键苹果。putIfAbsent方法不会改变其当前值。
At the same time, the key pear is missing from the map. Hence, it will be added.
同时,地图中缺少pear这个键。因此,它将被添加.。
4.4. The compute Method
4.4.compute方法
The compute method updates the value of a key based on the BiFunction provided as the second parameter. If the key doesn’t exist on the map, we can expect a NullPointerException.
compute方法根据作为第二个参数提供的BiFunction更新了一个键的值。如果该键在地图上不存在,我们可以期待一个NullPointerException。
Let’s check this method’s behavior with a simple test:
让我们用一个简单的测试来检查这个方法的行为。
@Test
public void givenFruitMap_whenComputeUsed_thenValueUpdated() {
double oldPrice = fruitMap.get("apple");
BiFunction<Double, Integer, Double> powFunction = (x1, x2) -> Math.pow(x1, x2);
fruitMap.compute("apple", (k, v) -> powFunction.apply(v, 2));
Assertions.assertEquals(
Double.valueOf(Math.pow(oldPrice, 2)), fruitMap.get("apple"));
Assertions.assertThrows(
NullPointerException.class, () -> fruitMap.compute("blueberry", (k, v) -> powFunction.apply(v, 2)));
}
As expected, since the key apple exists, its value in the map will be updated. On the other hand, there is no key blueberry, so the second call to the compute method in the last assertion will result in a NullPointerException.
正如预期的那样,由于键apple存在,它在地图中的值将被更新。另一方面,没有键blueberry,所以在最后一个断言中对compute方法的第二次调用将导致NullPointerException。
4.5. The computeIfAbsent Method
4.5.computeIfAbsent方法
The previous method throws an exception if there’s no pair in the HashMap for a specific key. The computeIfAbsent method will update the map by adding a (key, value) pair if it doesn’t exist.
如果在HashMap中没有特定键的配对,前面的方法会抛出一个异常。computeIfAbsent方法将通过添加(key, value)对来更新地图,如果它不存在。
Let’s test the behavior of this method:
让我们测试一下这个方法的行为。
@Test
public void givenFruitMap_whenComputeIfAbsentUsed_thenNewEntriesAdded() {
fruitMap.computeIfAbsent("lemon", k -> Double.valueOf(k.length()));
Assertions.assertTrue(fruitMap.containsKey("lemon"));
Assertions.assertEquals(Double.valueOf("lemon".length()), fruitMap.get("lemon"));
}
The key lemon doesn’t exist on the map. Hence, the computeIfAbsent method adds an entry.
键lemon在地图上并不存在。因此,computeIfAbsent方法添加了一个条目。
4.6. The computeIfPresent Method
4.6.computeIfPresent方法
The computeIfPresent method updates the value of a key if it is present in the HashMap.
computeIfPresent方法如果一个键在HashMap中存在,则更新该键的值。
Let’s see how we can use this method:
让我们看看如何使用这个方法。
@Test
public void givenFruitMap_whenComputeIfPresentUsed_thenValuesUpdated() {
Double oldAppleValue = fruitMap.get("apple");
BiFunction<Double, Integer, Double> powFunction = (x1, x2) -> Math.pow(x1, x2);
fruitMap.computeIfPresent("apple", (k, v) -> powFunction.apply(v, 2));
Assertions.assertEquals(Double.valueOf(Math.pow(oldAppleValue, 2)), fruitMap.get("apple"));
}
The assertion will pass since the key apple is in the map, and the computeIfPresent method will update the value according to the BiFunction.
断言将通过,因为键apple在地图中,并且computeIfPresent方法将根据BiFunction更新值。
4.7. The merge Method
4.7.融合方法
The merge method updates the value of a key in the HashMap using the BiFunction if there is such a key. Otherwise, it will add a new (key, value) pair, with the value set to the value provided as the second argument to the method.
merge方法如果有这样的键,则使用BiFunction更新HashMap中某个键的值。否则,它将添加一个新的(key, value)对,其值设置为作为该方法的第二个参数提供的value。
So, let’s inspect the behavior of this method:
因此,让我们检查一下这个方法的行为。
@Test
public void givenFruitMap_whenMergeUsed_thenNewEntriesAdded() {
double defaultValue = 1.25;
BiFunction<Double, Integer, Double> powFunction = (x1, x2) -> Math.pow(x1, x2);
fruitMap.merge("apple", defaultValue, (k, v) -> powFunction.apply(v, 2));
fruitMap.merge("strawberry", defaultValue, (k, v) -> powFunction.apply(v, 2));
Assertions.assertTrue(fruitMap.containsKey("strawberry"));
Assertions.assertEquals(Double.valueOf(defaultValue), fruitMap.get("strawberry"));
Assertions.assertEquals(Double.valueOf(Math.pow(defaultValue, 2)), fruitMap.get("apple"));
}
The test first executes the merge method on the key apple. It’s already on the map, so its value will change. It will be a square of the defaultValue parameter that we passed to the method.
这个测试首先对键apple执行merge方法。它已经在地图上了,所以它的值会改变。它将是我们传递给该方法的defaultValue参数的一个平方。
The key strawberry is not present on the map. Therefore, the merge method will add it with defaultValue as the value.
关键词strawberry在地图上不存在。因此,merge方法将以defaultValue为值添加它。
5. Conclusion
5.总结
In this article, we described several ways to update the value associated with a key in a HashMap.
在这篇文章中,我们描述了几种更新与HashMap中的某个键相关的值的方法。
First, we started with the most common approaches. Then, we showed several methods that have been available since Java 8.
首先,我们从最常见的方法开始。然后,我们展示了从Java 8开始就有的几种方法。
As always, the code for these examples is available over on GitHub.
像往常一样,这些例子的代码可以在GitHub上找到over。