Java >> Java tutorial >  >> Tag >> HashMap

Opdater værdien forbundet med en nøgle i et HashMap

1. Oversigt

Denne vejledning vil gennemgå de forskellige metoder til opdatering af værdien forbundet med en given nøgle i et HashMap . Først vil vi se på nogle almindelige løsninger, der kun bruger de funktioner, der var tilgængelige før Java 8. Derefter vil vi se på nogle yderligere løsninger, der er tilgængelige i Java 8 og nyere.

2. Initialiserer vores eksempel HashMap

For at vise, hvordan man opdaterer værdierne i et HashMap , skal vi oprette og udfylde en først. Så vi laver et kort med frugter som nøgler og deres priser som værdier:

Map<String, Double> priceMap = new HashMap<>();
priceMap.put("apple", 2.45);
priceMap.put("grapes", 1.22);

Vi bruger dette HashMap gennem hele vores eksempel. Nu er vi klar til at blive fortrolige med metoderne til opdatering af værdien forbundet med et HashMap nøgle.

3. Før Java 8

Lad os starte med de metoder, der var tilgængelige før Java 8.

3.1. put Metode

The put metode enten opdaterer værdien eller tilføjer en ny post . Hvis den bruges med en nøgle, der allerede eksisterer, så put metode vil opdatere den tilknyttede værdi. Ellers tilføjer den en ny (nøgle, værdi) par.

Lad os teste adfærden af ​​denne metode med to hurtige eksempler:

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

Nøglen æble er allerede på kortet. Derfor vil den første påstand bestå.

Siden orange ikke er til stede på kortet, put metode vil tilføje det. Derfor vil de to andre påstande også bestå.

3.2. Kombinationen af ​​containsKey og sæt Metoder

Kombinationen af ​​containsKey og sæt metoder er en anden måde at opdatere værdien af ​​en nøgle i HashMap . Denne mulighed kontrollerer, om kortet allerede indeholder en nøgle. I et sådant tilfælde kan vi opdatere værdien ved hjælp af put metode . Ellers kan vi enten tilføje en post til kortet eller ikke gøre noget.

I vores tilfælde vil vi inspicere denne tilgang med en simpel 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"));
}

Siden æble er på kortet, containsKey  metode vil returnere true . Derfor kaldes opkaldet til put metoden vil blive udført, og værdien vil blive opdateret.

4. Java 8 og nyere

Siden Java 8 er der mange nye metoder tilgængelige, der letter processen med at opdatere værdien af ​​en nøgle i HashMap. Så lad os lære dem at kende.

4.1. erstat  Metoder

To overbelastede erstat metoder har været tilgængelige på kortet interface siden version 8. Lad os se på metodesignaturerne:

public V replace(K key, V value);
public boolean replace(K key, V oldValue, V newValue);

Den første erstat metode tager kun en nøgle og en ny værdi. Det returnerer også den gamle værdi.

Lad os se, hvordan metoden virker:

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

Værdien af ​​nøglen æble vil blive opdateret til en ny pris med erstat metode. Derfor vil den anden og den tredje påstand bestå.

Men den første påstand er interessant . Hvad hvis der ikke var nogen nøgle æble i vores HashMap ? Hvis vi forsøger at opdatere værdien af ​​en ikke-eksisterende nøgle, null vil blive returneret. Når man tager det i betragtning, opstår der et andet spørgsmål:Hvad hvis der var en nøgle med null værdi? Vi kan ikke vide, om denne værdi er returneret fra erstat metode var faktisk værdien af ​​den angivne nøgle, eller hvis vi har forsøgt at opdatere værdien af ​​en ikke-eksisterende nøgle.

Så for at undgå misforståelser kan vi bruge den anden erstat metode. Det kræver tre argumenter:

  • en nøgle
  • den aktuelle værdi knyttet til nøglen
  • den nye værdi, der skal knyttes til nøglen

Den opdaterer værdien af ​​en nøgle til en ny værdi på én betingelse: Hvis det andet argument er den aktuelle værdi, vil nøgleværdien blive opdateret til en ny værdi. Metoden returnerer true for en vellykket opdatering. Ellers false er returneret.

Så lad os implementere nogle tests for at kontrollere den anden erstatning metode:

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

Siden den første test kalder erstat metode med den aktuelle værdi af nøglen, vil denne værdi blive erstattet.

På den anden side startes den anden test ikke med den aktuelle værdi. Altså falsk er returneret.

4.2. Kombinationen af ​​getOrDefault  og sæt M etoder

getOrDefault metode er et perfekt valg hvis vi ikke har en indtastning for den angivne nøgle . I så fald indstiller vi standardværdien for en ikke-eksisterende nøgle. Derefter føjes posten til kortet. Med denne tilgang kan vi nemt undslippe NullPointerException .

Lad os prøve denne kombination med en tast, der ikke oprindeligt er på kortet:

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

Da der ikke er en sådan nøgle, er getOrDefault metoden returnerer standardværdien. Derefter put metode vil tilføje et nyt (nøgle, værdi) par. Derfor vil alle påstande bestå.

4.3. putIfAbsent Metode

putIfAbsent metoden gør det samme som den tidligere kombination af getOrDefault og sæt metoder.

Hvis der ikke er et par i HashMap med den medfølgende nøgle, putIfAbsent metode vil tilføje parret. Men hvis der er et sådant par, vises putIfAbsent metoden ændrer ikke kortet.

Men der er en undtagelse:Hvis det eksisterende par har en nul værdi, så vil parret blive opdateret til en ny værdi.

Lad os implementere testen for putIfAbsent metode. Vi tester adfærden med to eksempler:

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

Et nøgle æble  er til stede på kortet. putIfAbsent metoden ændrer ikke dens nuværende værdi.

Samtidig er nøglen pære mangler på kortet. Derfor vil den blive tilføjet.

4.4. beregningen Metode

Compute  metode opdaterer værdien af ​​en nøgle baseret på BiFunction angivet som den anden parameter . Hvis nøglen ikke findes på kortet, kan vi forvente en NullPointerException .

Lad os tjekke denne metodes adfærd med en simpel 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)));
}

Som forventet, da nøglen æble eksisterer, vil dens værdi på kortet blive opdateret. På den anden side er der ingen nøgle blåbær , så det andet kald til compute metode i den sidste påstand vil resultere i en NullPointerException .

4.5. computeIfAbsent  Metode

Den tidligere metode giver en undtagelse, hvis der ikke er et par i HashMap for en bestemt nøgle. computeIfAbsent metode opdaterer kortet ved at tilføje en (nøgle, værdi) par, hvis det ikke findes .

Lad os teste adfærden af ​​denne metode:

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

Nøglen citron findes ikke på kortet. Derfor er komp uteIfAbsent metode tilføjer en post.

4.6. computeIfPresent Metode

computeIfPresent  metode opdaterer værdien af ​​en nøgle, hvis den er til stede i HashMap .

Lad os se, hvordan vi kan bruge denne metode:

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

Påstanden vil passere siden nøglen æble er på kortet, og computeIfPresent metoden opdaterer værdien i henhold til BiFunction .

4.7. sammenfletningen Metode

sammenfletningen metode opdaterer værdien af ​​en nøgle i HashMap ved hjælp af BiFunction hvis der er sådan en nøgle. Ellers tilføjer den en ny (nøgle, værdi) par, med værdien indstillet til værdien angivet som det andet argument til metoden.

Så lad os inspicere adfærden af ​​denne metode:

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

Testen udfører først fletningen  metode på nøglen æble . Det er allerede på kortet, så dets værdi vil ændre sig. Det vil være en firkant af defaultValue parameter, som vi har videregivet til metoden.

Nøglen jordbær er ikke til stede på kortet. Derfor er fletningen metode vil tilføje den med defaultValue som værdien.

5. Konklusion

I denne artikel har vi beskrevet flere måder at opdatere den værdi, der er knyttet til en nøgle i et HashMap .

Først startede vi med de mest almindelige tilgange. Derefter viste vi flere metoder, der har været tilgængelige siden Java 8.

Som altid er koden til disse eksempler tilgængelig på GitHub.


Java tag