Annexe B. Mises à jour de diverses bibliothèques

Cette annexe passe en revue les principaux ajouts à la bibliothèque Java.

B.1. Collections

La plus grande mise à jour de l’API Collections est l’introduction de flux, dont nous avons discuté dans les chapitres 4-6. Il existe également d’autres mises à jour, que nous examinons brièvement dans cette annexe.

B.1.1. Méthodes supplémentaires

Les concepteurs d’API Java ont tiré le meilleur parti des méthodes par défaut et ont ajouté plusieurs nouvelles méthodes aux interfaces et aux classes de collection. Les nouvelles méthodes sont répertoriées dans le tableau B.1.

Tableau B.1. Nouvelles méthodes ajoutées aux classes de collection et aux interfaces

L’interface Map est celle la plus à jour, avec le support de plusieurs nouvelles méthodes pratiques. Par exemple, la méthode getOrDefault peut être utilisée pour remplacer un idiome existant qui vérifie si une Map contient une valeur pour une clé donnée. Sinon, vous pouvez indiquer une valeur par défaut à retourner à la place. Auparavant, vous feriez ceci:

Vous pouvez maintenant faire plus simplement ce qui suit:

Notez que cela ne fonctionne que s’il n’y a pas de mapping. Par exemple, si la clé est explicitement mappée à la valeur null, aucune valeur par défaut ne sera renvoyée.

Une autre méthode particulièrement utile est computeIfAbsent, que nous avons brièvement mentionnée au chapitre 14 pour expliquer la mémoisation. Il vous permet d’utiliser commodément le motif de mise en cache. Disons que vous devez récupérer et traiter des données provenant de différents sites Web. Dans un tel scénario, il est utile de mettre en cache les données, de sorte que vous n’ayez pas à exécuter l’opération de récupération (coûteuse) plusieurs fois:

Vous pouvez maintenant écrire ce code de manière plus concise en utilisant computeIfAbsent comme suit:

Une description de toutes les autres méthodes peut être trouvée dans la documentation officielle de l’API Java. Notez que ConcurrentHashMap a également été mis à jour avec des méthodes supplémentaires. Nous en discutons dans la section B.2.

Collection

La méthode removeIf peut être utilisée pour supprimer tous les éléments d’une collection qui correspondent à un prédicat. Notez que cela est différent de la méthode de filtrage incluse dans l’API Streams. La méthode filter dans l’API Streams produit un nouveau flux; il ne mute pas le flux ou la source en cours.

List

La méthode replaceAll remplace chaque élément d’une liste par le résultat de l’application d’un opérateur donné. Il est similaire à la méthode map dans un flux, mais il mute les éléments de la liste. En revanche, la méthode de Map produit de nouveaux éléments.

Par exemple, le code suivant imprime [2, 4, 6, 8, 10] car la liste est modifiée directement:

B.1.2. La classe Collections

La classe Collections existe depuis longtemps pour exploiter ou retourner des collections. Il inclut maintenant des méthodes supplémentaires pour retourner NavigableMap et NavigableSet non modifiables, synchronisées, vérifiées et vides. En outre, il inclut la méthode checkedQueue, qui renvoie une vue de la file d’attente étendue avec la vérification de type dynamique.

B.1.3. Comparateur

L’interface Comparator inclut désormais des méthodes par défaut et des méthodes statiques. Vous avez utilisé la méthode statique Comparator.comparing dans le chapitre 3 pour renvoyer un objet Comparateur avec une fonction qui extrait la clé de tri.

Les nouvelles méthodes d’instance sont les suivantes:

  •     reversed-Renvoie un comparateur avec l’ordre inverse du comparateur actuel.
  •     thenComparing: renvoie un comparateur qui utilise un autre comparateur lorsque deux objets sont égaux.
  •     thenComparingInt, thenComparingDouble, thenComparingLong-Travaillent toutes comme la méthode thenComparing mais prennent une fonction spécialisée pour les types primitifs (respectivement, ToIntFunction, ToDoubleFunction et ToLongFunction).

Les nouvelles méthodes statiques incluent celles-ci:

  •     comparisonInt, compareDouble, compareLong- travaillent comme la méthode comparing mais prend une fonction spécialisée pour les types primitifs (respectivement ToIntFunction, ToDoubleFunction et ToLongFunction).
  •     naturalOrder: renvoie un objet Comparator qui impose un ordre naturel aux objets Comparable.
  •     nullsFirst, nullsLast-Retourne un objet Comparator qui considère que null est inférieur à null ou supérieur à non-null.
  •     reverseOrder – Équivalent à naturalOrder (). reversed ().

B.2. Concurrence

Java 8 apporte plusieurs mises à jour liées à la concurrence. La première est, bien sûr, l’introduction de flux parallèles, que nous explorons au chapitre 7. Il y a aussi l’introduction de la classe CompletableFuture, dont vous pouvez vous informer au chapitre 11.

Il y a d’autres mises à jour notables. Par exemple, la classe Arrays prend désormais en charge les opérations parallèles. Nous discutons de ces opérations dans la section B.3.

Dans cette section, nous examinons les mises à jour du paquetage java.util.concurrent.atomic, qui traite des variables atomiques. En outre, nous discutons des mises à jour de la classe ConcurrentHashMap, qui prend en charge plusieurs nouvelles méthodes.

B.2.1. Atomique

Le package java.util.concurrent.atomic offre plusieurs classes numériques, telles que AtomicInteger et AtomicLong qui prennent en charge l’opération atomique sur des variables uniques. Ils ont été mis à jour pour supporter de nouvelles méthodes:

  •     getAndUpdate: met à jour de manière atomique la valeur en cours avec les résultats de l’application de la fonction donnée, en renvoyant la valeur précédente.
  •     updateAndGet-Met à jour de manière atomique la valeur actuelle avec les résultats de l’application de la fonction donnée, en retournant la valeur mise à jour.
  •     getAndAccumulate: met à jour de manière atomique la valeur en cours avec les résultats de l’application de la fonction donnée aux valeurs actuelles, en retournant la valeur précédente.
  •     accumulateAndGet: met à jour de manière atomique la valeur en cours avec les résultats de l’application de la fonction donnée aux valeurs actuelles, en renvoyant la valeur mise à jour.

Voici comment définir atomiquement le minimum entre une valeur observée de 10 et un entier atomique existant:

Adders et accumulateurs

L’API Java recommande d’utiliser les nouvelles classes LongAdder, LongAccumulator, Double-Adder et DoubleAccumulator au lieu des classes atomiques équivalentes lorsque plusieurs threads sont mis à jour fréquemment mais lus moins fréquemment (par exemple, dans le contexte des statistiques). Ces classes sont conçues pour croître dynamiquement afin de réduire les conflits de threads.

Les classes LongAdder et DoubleAdder supportent les opérations pour les ajouts, alors que LongAccumulator et DoubleAccumulator ont une fonction pour combiner les valeurs. Par exemple, pour calculer la somme de plusieurs valeurs, vous pouvez utiliser un LongAdder comme suit.

Listing B.1. LongAdder pour calculer la somme des valeurs

Ou vous pouvez utiliser un LongAccumulator comme suit.

Listing B.2. LongAccumulator pour calculer la somme des valeurs

B.2.2. ConcurrentHashMap

La classe ConcurrentHashMap a été introduite pour fournir un HashMap plus moderne, qui est conciliant. ConcurrentHashMap permet l’ajout simultané et les mises à jour qui verrouillent uniquement certaines parties de la structure de données interne. Ainsi, les opérations de lecture et d’écriture ont des performances améliorées par rapport à l’alternative Hashtable synchronisée.

Performance

La structure interne de ConcurrentHashMap a été mise à jour pour améliorer les performances. Les entrées d’une Map sont généralement stockées dans des compartiments auxquels accède le hashcode généré de la clé. Mais si de nombreuses clés renvoient le même code, les performances se détérioreront car les compartiments sont implémentés en tant que listes avec récupération O (n). Dans Java 8, lorsque les compartiments deviennent trop grands, ils sont remplacés dynamiquement par des arbres triés, qui ont une récupération O (log (n)). Notez que cela n’est possible que lorsque les clés sont comparables (par exemple, les classes String ou Number).

Opérations de type Stream

ConcurrentHashMap prend en charge trois nouveaux types d’opérations qui rappellent ce que vous avez vu avec les flux:

  •     forEach-Effectue une action donnée pour chaque (clé, valeur)
  •     reduce-Combine tout (clé, valeur) en fonction d’une fonction de réduction dans un résultat
  •     search-Applique une fonction sur chaque (clé, valeur) jusqu’à ce que la fonction produise un résultat non nul

Chaque type d’opération prend en charge quatre formulaires, acceptant des fonctions avec des clés, des valeurs, Map.Entry, et des arguments (clé, valeur):

  •     Fonctionne avec les clés et les valeurs (forEach, reduce, search)
  •     Fonctionne avec les clés(forEachKey, reduceKeys, searchKeys)
  •     Fonctionne avec des valeurs (forEachValue, reduceValues, searchValues)
  •     Fonctionne avec les objets Map.Entry (forEachEntry, reduceEntries, searchEntries)

Notez que ces opérations ne verrouillent pas l’état du ConcurrentHashMap. Ils agissent sur les éléments au fur et à mesure. Les fonctions fournies à ces opérations ne doivent dépendre d’aucun ordre ou d’autres objets ou valeurs qui puissent changer pendant le calcul.

De plus, vous devez spécifier un seuil de parallélisme pour toutes ces opérations. Les opérations s’exécuteront séquentiellement si la taille de la carte actuelle est estimée inférieure au seuil donné. L’utilisation d’une valeur de 1 permet un parallélisme maximal en utilisant le pool de threads communs. L’utilisation d’une valeur de Long.MAX_VALUE exécute l’opération sur un seul thread.

Dans cet exemple, nous utilisons la méthode reduceValues ​​pour trouver la valeur maximale dans la Map:

Notez qu’il existe des spécialisations primitives pour int, long et double pour chaque opération de réduction (par exemple, reduceValuesToInt, reduceKeysToLong, etc.).

Compte

La classe ConcurrentHashMap fournit une nouvelle méthode appelée mappingCount, qui renvoie le nombre de mapping dans la Map en tant que long. Il devrait être utilisé à la place de la taille de la méthode, qui renvoie un int. C’est parce que le nombre de mappages peut ne pas tenir dans un int.

Définir des vues

La classe ConcurrentHashMap fournit une nouvelle méthode appelée keySet qui renvoie une vue du ConcurrentHashMap en tant que Set(les modifications apportées à la Map sont reflétées dans l’ensemble et vice versa). Vous pouvez également créer un ensemble soutenu par un ConcurrentHashMap en utilisant la nouvelle méthode statique newKeySet.

B.3. Tableaux

La classe Arrays fournit diverses méthodes statiques pour manipuler les tableaux. Il comprend maintenant quatre nouvelles méthodes (qui ont des suppléments de spécialités primitives).

B.3.1. Utiliser parallelSort

La méthode parallelSort trie le tableau spécifié en parallèle, en utilisant un ordre naturel, ou en utilisant un comparateur supplémentaire pour un tableau d’objets.

B.3.2. À l’aide de setAll et parallelSetAll

Les méthodes setAll et parallelSetAll initialise tous les éléments du tableau, soit séquentiellement ou en parallèle, en utilisant la fonction fournie pour calculer chaque élément. La fonction reçoit l’index de l’élément et renvoie une valeur pour cet index. Comme parallelSetAll est exécuté en parallèle, la fonction doit être sans effets secondaires, comme expliqué dans les chapitres 7 et 13.

Un titre par exemple, vous pouvez utiliser la méthode setAll pour produire un tableau avec les valeurs 0, 2, 4, 6, …:

B.3.3. Utilisation de parallelPrefix

La méthode parallelPrefix cumule, en parallèle, chaque élément du tableau donné, en utilisant l’opérateur binaire fourni. Dans la liste suivante, vous produisez les valeurs 1, 2, 3, 4, 5, 6, 7, ….

Figure B.3. parallelPrefix se cumule dans les éléments parallèles d’un tableau

B.4. Nombre et Mathématiques

L’API Java 8 améliore les classes Number et Math avec de nouvelles méthodes.

B.4.1. Nombre

Les nouvelles méthodes de la classe Number sont les suivantes:

  •     Les classes Short, Integer, Long, Float et Double incluent les méthodes statiques sum, min et max. Vous avez vu ces méthodes en conjonction avec l’opération reduce au chapitre 5.
  •     Les classes Integer et Long incluent les méthodes compareUnsigned, divideUnsigned, remainderUnsigned et toUnsignedString pour fonctionner avec des valeurs non signées.
  •     Les classes Integer et Long incluent également respectivement les méthodes statiques parse-UnsignedInt et parseUnsignedLong, pour analyser les chaînes comme un entier non signé ou long.
  •     Les classes Byte et Short incluent les méthodes toUnsignedInt et toUnsigned-Long, pour convertir l’argument en int ou long par une conversion non signée. De même, la classe Integer inclut désormais la méthode statique toUnsignedLong.
  • Les classes Double et Float incluent la méthode statique isFinite, pour vérifier si l’argument est une valeur à virgule flottante finie.
  • La classe Booléenne inclut désormais les méthodes statiques logicalAnd, logicalOr et logicalXor, pour appliquer les opérations and, or et xor entre deux booléens.
    La classe BigInteger inclut les méthodes byteValueExact, shortValueExact, intValueExact et longValueExact, pour convertir BigInteger au type primitif correspondant. Mais il jette une exception arithmétique s’il y a une perte d’information lors de la conversion.

B.4.2. Math

La classe Math inclut de nouvelles méthodes qui lancent une exception arithmétique si le résultat de l’opération déborde. Ces méthodes comprennent addExact, subtractExact, multiplier-Exact, incrementExact, decrementExact et negateExact avec int et long arguments. En outre, il existe une méthode statique toIntExact pour convertir une valeur long en int. D’autres ajouts incluent les méthodes statiques floorMod, floorDiv et nextDown.

B.5. Fichiers

Des ajouts notables à la classe Files vous permettent de produire un flux à partir de fichiers. Nous avons mentionné la nouvelle méthode statique Files.lines au chapitre 5; elle vous permet de lire un fichier paresseusement comme un flux. D’autres méthodes statiques utiles renvoyant un flux sont les suivantes:

  •     Files.list-Produit un Stream<Path> composé d’entrées dans un répertoire donné. La liste n’est pas récursive. Comme le flux est consommé paresseusement, c’est une méthode utile pour traiter des répertoires potentiellement très volumineux.
  •     Files.walk-Tout comme Files.list, il produit un Stream <Path> composé d’entrées dans un répertoire donné. Mais la liste est récursive et le niveau de profondeur peut être configuré. Notez que la traversée est effectuée en profondeur en premier.
  •     Files.find-Produit une Stream<Path> à partir de parcourir récursivement un répertoire pour trouver les entrées qui correspondent à un prédicat donné.

B.6. Réflexion

Nous avons discuté de plusieurs modifications du mécanisme d’annotation dans Java 8 dans l’annexe A. L’API Reflection a été mise à jour pour prendre en charge ces modifications.

Un autre ajout à l’API Reflection est que les informations sur les paramètres de méthodes telles que les noms et les modificateurs peuvent désormais être consultées à l’aide de la nouvelle classe java.lang.reflect.Parameter, référencée dans la nouvelle classe .java.lang .reflect.Executable qui sert de superclasse partagée pour des fonctionnalités communes comme Method et Constructor.

B.7. String

La classe String inclut maintenant une méthode statique pratique appelée join à -comme vous pouvez le deviner -joindre des String avec un délimiteur! Vous pouvez l’utiliser comme suit: