Heim  >  Artikel  >  Java  >  Detaillierte Einführung von Map.merge() (mit Code)

Detaillierte Einführung von Map.merge() (mit Code)

不言
不言nach vorne
2019-03-22 17:04:096009Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Einführung in Map.merge() (mit Code). Freunde in Not können darauf verweisen.

Heute stellen wir die Merge-Methode von Map vor.

In der JDK-API ist eine solche Methode etwas ganz Besonderes, sie ist sehr neu und es lohnt sich, sie zu verstehen. Es wird auch empfohlen, sie auf tatsächlichen Projektcode anzuwenden von großer Hilfe. Map.merge()). Dies ist wahrscheinlich die vielseitigste Funktion in Maps. Aber es ist auch ziemlich unklar und nur wenige Leute nutzen es.

Einführung in den Hintergrund

merge() kann wie folgt erklärt werden: Es weist dem Schlüssel einen neuen Wert zu (falls dieser nicht existiert) oder aktualisiert einen vorhandenen Schlüssel mit einem bestimmten Wert (UPSERT). . Beginnen wir mit dem einfachsten Beispiel: dem Zählen eindeutiger Wortvorkommen. Vor Java 8 war der Code sehr verwirrend und die eigentliche Implementierung hatte tatsächlich ihre wesentliche Designbedeutung verloren.

var map = new HashMap<String, Integer>();
words.forEach(word -> {
    var prev = map.get(word);
    if (prev == null) {
        map.put(word, 1);
    } else {
        map.put(word, prev + 1);
    }
});

Gemäß der Logik des obigen Codes ist das Ausgabeergebnis unter der Annahme, dass ein Eingabesatz angegeben ist, wie folgt; , hauptsächlich das Entfernen einiger seiner Beurteilungslogik; Verbesserungen wie

var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz");
//...
{Bar=1, Fizz=2, Foo=3, Buzz=2}

können unsere Rekonstruktionsanforderungen erfüllen. Die spezifische Verwendung von putIfAbsent() wird nicht im Detail beschrieben. Die Codezeile putIfAbsent wird unbedingt benötigt, andernfalls meldet die nachfolgende Logik einen Fehler. Im folgenden Code ist es seltsam, dass Put und Get erneut auftauchen. Lassen Sie uns das Design weiter verbessern.

V2 verbessern

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.put(word, map.get(word) + 1);
});

computeIfPresent soll die angegebene Transformation nur aufrufen, wenn der Schlüssel im Wort vorhanden ist. Ansonsten wird nichts verarbeitet. Wir stellen sicher, dass der Schlüssel existiert, indem wir ihn auf Null initialisieren, sodass das Inkrement immer gültig ist. Ist diese Implementierung perfekt genug? Nicht unbedingt, es gibt andere Ideen, um die zusätzliche Initialisierung zu reduzieren.

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.computeIfPresent(word, (w, prev) -> prev + 1);
});

compute() ist wie computeIfPresent(), wird jedoch unabhängig von der Anwesenheit oder Abwesenheit des angegebenen Schlüssels aufgerufen. Wenn der Wert von key nicht vorhanden ist, ist der prev-Parameter null. Das Verschieben eines einfachen if in einen ternären Ausdruck, der in einem Lambda versteckt ist, ist ebenfalls alles andere als eine optimale Leistung. Bevor ich Ihnen die endgültige Version zeige, werfen wir einen Blick auf eine leicht vereinfachte Quellcode-Analyse der Standardimplementierung von Map.merge().

V3 verbessern

Merge()-Quellcode

words.forEach(word ->
        map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);

Codefragmente sagen mehr als tausend Worte. Sie können jederzeit neue Länder entdecken, indem Sie den Quellcode lesen. merge() ist für beide Situationen geeignet. Wenn der angegebene Schlüssel nicht existiert, wird er zu put(key, value). Wenn der Schlüssel jedoch bereits einige Werte hat, kann unsere remappingFunction die Merge-Methode auswählen. Diese Funktion ist perfekt für das obige Szenario:

Geben Sie einfach den neuen Wert zurück, um den alten Wert zu überschreiben:

    Geben Sie einfach den alten Wert zurück, um den alten Wert beizubehalten:
  • (old, new) -> new
  • Fügen Sie die beiden auf irgendeine Weise zusammen, wie zum Beispiel:
  • (old, new) -> old
  • Oder entfernen Sie sogar den alten Wert:
  • (old, new) -> old + new
  • Wie Sie sehen können, it merge() ist sehr allgemein. Unsere Frage ist also, wie man merge() verwendet. Der Code lautet wie folgt: (old, new) -> null
    default V merge(K key, V value, BiFunction<V, V, V> remappingFunction) {
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if (newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }
  • Sie können ihn wie folgt verstehen: Wenn kein Schlüssel vorhanden ist, ist der initialisierte Wert gleich 1, andernfalls wird 1 zum vorhandenen Wert addiert. Eine im Code ist eine Konstante, da in unserem Szenario der Standardwert immer plus 1 ist und die spezifischen Änderungen nach Belieben geändert werden können.

Szenario

Stellen Sie sich vor, ist merge() wirklich so einfach zu verwenden? Was können seine Szenen sein?

Geben Sie ein Beispiel. Sie haben eine Kontooperationsklasse

words.forEach(word ->
        map.merge(word, 1, (prev, one) -> prev + one)
);

und eine Reihe von Operationen für verschiedene Konten:

class Operation {
    private final String accNo;
    private final BigDecimal amount;
}

Wir möchten den Saldo (Gesamtbetriebsbetrag) für jedes Konto berechnen. Wenn merge() nicht verwendet wird, wird es sehr problematisch:

operations = List.of(
    new Operation("123", new BigDecimal("10")),
    new Operation("456", new BigDecimal("1200")),
    new Operation("123", new BigDecimal("-4")),
    new Operation("123", new BigDecimal("8")),
    new Operation("456", new BigDecimal("800")),
    new Operation("456", new BigDecimal("-1500")),
    new Operation("123", new BigDecimal("2")),
    new Operation("123", new BigDecimal("-6.5")),
    new Operation("456", new BigDecimal("-600"))
);

Verwenden Sie den Code nach merge

Map balances = new HashMap<String, BigDecimal>();
operations.forEach(op -> {
    var key = op.getAccNo();
    balances.putIfAbsent(key, BigDecimal.ZERO);
    balances.computeIfPresent(key, (accNo, prev) -> prev.add(op.getAmount()));
});

, um die Logik zu optimieren.

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), 
                (soFar, amount) -> soFar.add(amount))
);

Natürlich ist das Ergebnis korrekt. Sind Sie von einem so prägnanten Code begeistert? Für jede Operation wird

innerhalb des gegebenen

gegebenen

angegeben.

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add)
);
addConcurrentHashMapamountaccNoWenn wir auf ConcurrentHashMap erweitern und Map.merge angezeigt wird, ist die Kombination mit ConcurrentHashMap sehr perfekt. Dieses Abgleichsszenario gilt für Single-Thread-sichere Logik, die automatisch Einfüge- oder Aktualisierungsvorgänge ausführt.

Dieser Artikel ist hier zu Ende. Weitere spannende Inhalte finden Sie in der Spalte

Java-Tutorial-Video

auf der chinesischen PHP-Website!

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung von Map.merge() (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen