Introduction à jOOL

Introduction à jOOL

1. Vue d'ensemble

Dans cet article, nous examinerons la bibliothèquejOOL __ - un autre produit dejOOQ.

2. Dépendance Maven

Commençons par ajouter une dépendance Maven à vospom.xml:


    org.jooq
    jool
    0.9.12

Vous pouvez trouver la dernière versionhere.

3. Interfaces fonctionnelles

En Java 8, les interfaces fonctionnelles sont assez limitées. Ils acceptent le nombre maximal de deux paramètres et n'ont pas beaucoup de fonctionnalités supplémentaires.

jOOL corrige cela en prouvant un ensemble de nouvelles interfaces fonctionnelles qui peuvent accepter même 16 paramètres (deFunction1 àFunction16) et sont enrichies de méthodes pratiques supplémentaires.

Par exemple, pour créer une fonction qui prend trois arguments, nous pouvons utiliserFunction3:

Function3 lengthSum
  = (v1, v2, v3) -> v1.length() + v2.length() + v3.length();

En Java pur, vous devez le mettre en œuvre vous-même. De plus, les interfaces fonctionnelles de jOOL ont une méthodeapplyPartially() qui nous permet d'effectuer facilement une application partielle:

Function2 addTwoNumbers = (v1, v2) -> v1 + v2;
Function1 addToTwo = addTwoNumbers.applyPartially(2);

Integer result = addToTwo.apply(5);

assertEquals(result, (Integer) 7);

Lorsque nous avons une méthode de typeFunction2, nous pouvons la transformer facilement en une méthode JavaBiFunction standard en utilisant une méthodetoBiFunction():

BiFunction biFunc = addTwoNumbers.toBiFunction();

De même, il existe une méthodetoFunction() de typeFunction1.

4. Tuples

Un tuple est une construction très importante dans un monde de programmation fonctionnelle. Il s’agit d’un conteneur typé pour les valeurs où chaque valeur peut avoir un type différent. Tuples are often used as function arguments.

Ils sont également très utiles pour effectuer des transformations sur un flux d’événements. Dans jOOL, nous avons des tuples qui peuvent aller de une à seize valeurs, fournies par les typesTuple1 jusqu'àTuple16:

tuple(2, 2)

Et pour quatre valeurs:

tuple(1,2,3,4);

Prenons un exemple lorsque nous avons une séquence de tuples qui portait 3 valeurs:

Seq> personDetails = Seq.of(
  tuple("michael", "similar", 49),
  tuple("jodie", "variable", 43));
Tuple2 tuple = tuple("winter", "summer");

List> result = personDetails
  .map(t -> t.limit2().concat(tuple)).toList();

assertEquals(
  result,
  Arrays.asList(tuple("michael", "similar", "winter", "summer"), tuple("jodie", "variable", "winter", "summer"))
);

Nous pouvons utiliser différents types de transformations sur les n-uplets. Tout d'abord, nous appelons une méthodelimit2() pour ne prendre que deux valeurs deTuple3. Ensuite, nous appelons une méthodeconcat() pour concaténer deux tuples.

Dans le résultat, nous obtenons des valeurs de typeTuple4.

5. Seq

La constructionSeq ajoute des méthodes de plus haut niveau sur unStream alors qu'elle utilise souvent ses méthodes en dessous.

5.1. Contient des opérations

Nous pouvons trouver quelques variantes de méthodes vérifiant la présence d'éléments dans unSeq. Certaines de ces méthodes utilisent une méthodeanyMatch() d'une classeStream:

assertTrue(Seq.of(1, 2, 3, 4).contains(2));

assertTrue(Seq.of(1, 2, 3, 4).containsAll(2, 3));

assertTrue(Seq.of(1, 2, 3, 4).containsAny(2, 5));

5.2. Rejoignez les opérations

Lorsque nous avons deux flux et que nous voulons les joindre (similaire à une opération de jointure SQL de deux ensembles de données), l'utilisation d'une classe standardStream n'est pas une manière très élégante de le faire:

Stream left = Stream.of(1, 2, 4);
Stream right = Stream.of(1, 2, 3);

List rightCollected = right.collect(Collectors.toList());
List collect = left
  .filter(rightCollected::contains)
  .collect(Collectors.toList());

assertEquals(collect, Arrays.asList(1, 2));

Nous devons collecter le flux deright dans une liste, pour empêcherjava.lang.IllegalStateException: stream has already been operated upon or closed. Ensuite, nous devons effectuer une opération d'effet secondaire en accédant à une listerightCollected à partir d'une méthodefilter. C'est une manière peu élégante et sujette aux erreurs de joindre deux ensembles de données.

Heureusement,Seq has useful methods to do inner, left and right joins on data sets. Ces méthodes cachent une implémentation de celle-ci exposant une API élégante.

Nous pouvons faire une jointure interne en utilisant une méthodeinnerJoin():

assertEquals(
  Seq.of(1, 2, 4).innerJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(),
  Arrays.asList(tuple(1, 1), tuple(2, 2))
);

Nous pouvons faire des jointures à droite et à gauche en conséquence:

assertEquals(
  Seq.of(1, 2, 4).leftOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(),
  Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(4, null))
);

assertEquals(
  Seq.of(1, 2, 4).rightOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(),
  Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(null, 3))
);

Il existe même une méthodecrossJoin() qui permet de faire une jointure cartésienne de deux jeux de données:

assertEquals(
  Seq.of(1, 2).crossJoin(Seq.of("A", "B")).toList(),
  Arrays.asList(tuple(1, "A"), tuple(1, "B"), tuple(2, "A"), tuple(2, "B"))
);

5.3. Manipuler unSeq

Seq a de nombreuses méthodes utiles pour manipuler des séquences d'éléments. Examinons certains d’entre eux.

Nous pouvons utiliser une méthodecycle() pour prendre à plusieurs reprises des éléments d'une séquence source. Cela créera un flux infini, nous devons donc faire attention lors de la collecte des résultats dans une liste, nous devons donc utiliser une méthodelimit() pour transformer une séquence infinie en une séquence finie:

assertEquals(
  Seq.of(1, 2, 3).cycle().limit(9).toList(),
  Arrays.asList(1, 2, 3, 1, 2, 3, 1, 2, 3)
);

Disons que nous voulons dupliquer tous les éléments d’une séquence à la deuxième séquence. La méthodeduplicate() fait exactement cela:

assertEquals(
  Seq.of(1, 2, 3).duplicate().map((first, second) -> tuple(first.toList(), second.toList())),
  tuple(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3))
);

Le type de retour d'une méthodeduplicate() est un tuple de deux séquences.

Disons que nous avons une séquence d’entiers et que nous voulons diviser cette séquence en deux séquences en utilisant un prédicat. Nous pouvons utiliser une méthodepartition():

assertEquals(
  Seq.of(1, 2, 3, 4).partition(i -> i > 2)
    .map((first, second) -> tuple(first.toList(), second.toList())),
  tuple(Arrays.asList(3, 4), Arrays.asList(1, 2))
);

5.4. Regroupement des éléments

Regrouper des éléments par une clé à l'aide de l'APIStream est fastidieux et peu intuitif - car nous devons utiliser la méthodecollect() avec un collecteurCollectors.groupingBy.

Seq cache ce code derrière une méthodegroupBy() qui renvoieMap donc il n'est pas nécessaire d'utiliser une méthodecollect() explicitement:

Map> expectedAfterGroupBy = new HashMap<>();
expectedAfterGroupBy.put(1, Arrays.asList(1, 3));
expectedAfterGroupBy.put(0, Arrays.asList(2, 4));

assertEquals(
  Seq.of(1, 2, 3, 4).groupBy(i -> i % 2),
  expectedAfterGroupBy
);

5.5. Sauter des éléments

Supposons que nous ayons une séquence d’éléments et que nous voulions sauter des éléments tant qu’un prédicat n’est pas mis en correspondance. Lorsqu'un prédicat est satisfait, les éléments doivent atterrir dans une séquence résultante.

Nous pouvons utiliser une méthodeskipWhile() pour cela:

assertEquals(
  Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3).toList(),
  Arrays.asList(3, 4, 5)
);

Nous pouvons obtenir le même résultat en utilisant une méthodeskipUntil():

assertEquals(
  Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3).toList(),
  Arrays.asList(3, 4, 5)
);

5.6. Zipper des séquences

Lorsque nous traitons des séquences d’éléments, il est souvent nécessaire de les compresser en une seule séquence.

L'APIzip() qui pourrait être utilisée pour compresser deux séquences en une seule:

assertEquals(
  Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c")).toList(),
  Arrays.asList(tuple(1, "a"), tuple(2, "b"), tuple(3, "c"))
);

La séquence résultante contient des nuplets de deux éléments.

Lorsque nous compressons deux séquences, mais que nous voulons les compresser d'une manière spécifique, nous pouvons passer unBiFunction à une méthodezip() qui définit la manière de compresser les éléments:

assertEquals(
  Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (x, y) -> x + ":" + y).toList(),
  Arrays.asList("1:a", "2:b", "3:c")
);

Parfois, il est utile de compresser la séquence avec un index des éléments de cette séquence, via l'APIzipWithIndex():

assertEquals(
  Seq.of("a", "b", "c").zipWithIndex().toList(),
  Arrays.asList(tuple("a", 0L), tuple("b", 1L), tuple("c", 2L))
);

6. Conversion d'exceptions vérifiées en non vérifiées

Disons que nous avons une méthode qui prend une chaîne et peut lever une exception vérifiée:

public Integer methodThatThrowsChecked(String arg) throws Exception {
    return arg.length();
}

Ensuite, nous voulons mapper les éléments d'unStream appliquant cette méthode à chaque élément. Il n'y a aucun moyen de gérer cette exception plus haut, nous devons donc gérer cette exception dans une méthodemap():

List collect = Stream.of("a", "b", "c").map(elem -> {
    try {
        return methodThatThrowsChecked(elem);
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}).collect(Collectors.toList());

assertEquals(
    collect,
    Arrays.asList(1, 1, 1)
);

Nous n’avons pas grand-chose à faire avec cette exception en raison de la conception d’interfaces fonctionnelles en Java. Dans une clause catch, nous convertissons une exception vérifiée en une exception non contrôlée.

Heureusement, dans un jOOL, il y a une classeUnchecked qui a des méthodes qui peuvent convertir les exceptions vérifiées en exceptions non vérifiées:

List collect = Stream.of("a", "b", "c")
  .map(Unchecked.function(elem -> methodThatThrowsChecked(elem)))
  .collect(Collectors.toList());

assertEquals(
  collect,
  Arrays.asList(1, 1, 1)
);

Nous intégrons un appel à unmethodThatThrowsChecked() dans une méthodeUnchecked.function() qui gère la conversion des exceptions en dessous.

7. Conclusion

Cet article montre comment utiliser la bibliothèque jOOL qui ajoute des méthodes supplémentaires utiles à l'API Java standardStream.

L'implémentation de tous ces exemples et extraits de code peut être trouvée dans leGitHub project - il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.