Maison > Java > javaDidacticiel > Introduction détaillée de Map.merge() (avec code)

Introduction détaillée de Map.merge() (avec code)

不言
Libérer: 2019-03-22 17:04:09
avant
6039 Les gens l'ont consulté

Cet article vous apporte une introduction détaillée à Map.merge() (avec code). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

Aujourd'hui, nous présentons la méthode de fusion de Map. Jetons un coup d'œil à sa puissance.

Dans l'API JDK, une telle méthode est très spéciale, elle est très nouvelle et cela vaut la peine de la comprendre. Il est également recommandé que vous puissiez l'appliquer au code du projet réel. d'une grande aide. Map.merge()). Il s’agit probablement de l’opération la plus polyvalente sur Maps. Mais c’est aussi assez obscur et peu de gens l’utilisent.

Introduction au contexte

merge() peut s'expliquer comme suit : il attribue une nouvelle valeur à la clé (si elle n'existe pas) ou met à jour une clé existante avec une valeur donnée (UPSERT) . Commençons par l'exemple le plus élémentaire : compter les occurrences de mots uniques. Avant Java 8, le code était très déroutant et l'implémentation réelle avait en fait perdu sa signification essentielle en termes de conception.

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);
    }
});
Copier après la connexion

Selon la logique du code ci-dessus, en supposant qu'un ensemble d'entrées soit donné, le résultat de sortie est le suivant

var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz");
//...
{Bar=1, Fizz=2, Foo=3, Buzz=2}
Copier après la connexion

Améliorer la V1

Maintenant ; reconstruisons Il supprime principalement une partie de sa logique de jugement

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.put(word, map.get(word) + 1);
});
Copier après la connexion

De telles améliorations peuvent répondre à nos exigences de reconstruction ; L'utilisation spécifique de putIfAbsent() ne sera pas décrite en détail. La ligne de code putIfAbsent est absolument nécessaire, sinon la logique suivante signalera une erreur. Dans le code ci-dessous, il est étrange que put et get apparaissent à nouveau. Continuons à améliorer le design.

Améliorer la V2

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.computeIfPresent(word, (w, prev) -> prev + 1);
});
Copier après la connexion

computeIfPresent consiste à appeler la transformation donnée uniquement lorsque la clé du mot existe. Sinon, ça ne gère rien. Nous nous assurons que la clé existe en l'initialisant à zéro, afin que l'incrément soit toujours valide. Cette mise en œuvre est-elle suffisamment parfaite ? Pas nécessairement, il existe d'autres idées pour réduire l'initialisation supplémentaire.

words.forEach(word ->
        map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);
Copier après la connexion

compute () est comme calculateIfPresent(), mais il est appelé quelle que soit la présence ou l'absence de la clé donnée. Si la valeur de key n'existe pas, le paramètre prev est nul. Déplacer un simple if vers une expression ternaire cachée dans un lambda est également loin d'être une performance optimale. Avant de vous montrer la version finale, jetons un coup d'œil à une analyse du code source légèrement simplifiée de l'implémentation par défaut de Map.merge().

Améliorer la V3

code source de merge()
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;
}
Copier après la connexion

Les extraits de code valent mille mots. Vous pouvez toujours découvrir de nouveaux terrains en lisant le code source. merge() convient aux deux situations. Si la clé donnée n'existe pas, elle devient put(key, value). Cependant, si la clé a déjà des valeurs, notre remappingFunction peut choisir de fusionner. Cette fonctionnalité est parfaite pour le scénario ci-dessus :

  • Renvoyez simplement la nouvelle valeur pour écraser l'ancienne valeur : (old, new) -> new
  • Renvoyez simplement l'ancienne valeur pour conserver l'ancienne valeur : (old, new) -> old
  • Fusionnez les deux d'une manière comme : (old, new) -> old + new
  • Ou même supprimez l'ancienne valeur : (old, new) -> null

Comme vous pouvez le voir, fusionner () est très général. Notre question est donc de savoir comment utiliser merge() ? Le code est le suivant :

words.forEach(word ->
        map.merge(word, 1, (prev, one) -> prev + one)
);
Copier après la connexion

Vous pouvez le comprendre ainsi : s'il n'y a pas de clé, alors la valeur initialisée est égale à 1 sinon, 1 est ajouté à la valeur existante ; Un dans le code est une constante, car dans notre scénario, la valeur par défaut est toujours plus 1 et des modifications spécifiques peuvent être modifiées à volonté.

Scénario

Imaginez, est-ce que merge() est vraiment si simple à utiliser ? Quelles peuvent être ses scènes ?

Donnez un exemple. Vous disposez d'une classe d'actions de compte

class Operation {
    private final String accNo;
    private final BigDecimal amount;
}
Copier après la connexion

et d'une série d'actions pour différents comptes :

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"))
);
Copier après la connexion

Nous souhaitons calculer le solde (montant total d'exploitation) pour chaque compte. Si merge() n'est pas utilisé, cela devient très gênant :

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()));
});
Copier après la connexion

Utilisez le code après la fusion

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), 
                (soFar, amount) -> soFar.add(amount))
);
Copier après la connexion

pour optimiser la logique.

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add)
);
Copier après la connexion

Bien sûr, le résultat est correct. Êtes-vous enthousiasmé par un code aussi concis ? Pour chaque opération, add est donné dans le amount donné accNo.

{ 123 = 9.5,456 = - 100 }
Copier après la connexion

ConcurrentHashMap

Lorsque nous étendons à ConcurrentHashMap, lorsque Map.merge apparaît, la combinaison avec ConcurrentHashMap est très parfaite. Ce scénario de correspondance concerne une logique monothread-safe qui effectue automatiquement des opérations d'insertion ou de mise à jour.

Cet article est terminé ici. Pour un contenu plus passionnant, vous pouvez prêter attention à la colonne Vidéo du didacticiel Java du site Web PHP chinois !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal