Maison >Java >javaDidacticiel >Introduction détaillée de Map.merge() (avec code)
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); } });
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}
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); });
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.
words.forEach(word -> { map.putIfAbsent(word, 0); map.computeIfPresent(word, (w, prev) -> prev + 1); });
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) );
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; }
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 :
(old, new) -> new
(old, new) -> old
(old, new) -> old + new
(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) );
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; }
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")) );
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())); });
Utilisez le code après la fusion
operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), (soFar, amount) -> soFar.add(amount)) );
pour optimiser la logique.
operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add) );
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 }
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!