Trouver le premier élément par prédicat

Je viens de commencer à jouer avec Java 8 et j’essaie d’implémenter certaines des fonctionnalités auxquelles je suis habitué dans les langages fonctionnels.

Par exemple, la plupart des langages fonctionnels ont une sorte de fonction de recherche qui opère sur des séquences, ou des listes qui renvoie le premier élément, pour lequel le prédicat est true . La seule façon que je peux voir pour y parvenir dans Java 8 est la suivante:

 lst.stream() .filter(x -> x > 5) .findFirst() 

Cependant, cela me semble inefficace, car le filtre parsingra toute la liste, du moins à ma connaissance (ce qui pourrait être faux). Y a-t-il une meilleure façon?

Non, le filtre n’parsing pas tout le stream. C’est une opération intermédiaire, qui renvoie un stream paresseux (en fait, toutes les opérations intermédiaires renvoient un stream différé). Pour vous convaincre, vous pouvez simplement faire le test suivant:

 List list = Arrays.asList(1, 10, 3, 7, 5); int a = list.stream() .peek(num -> System.out.println("will filter " + num)) .filter(x -> x > 5) .findFirst() .get(); System.out.println(a); 

Quelles sorties:

 will filter 1 will filter 10 10 

Vous voyez que seuls les deux premiers éléments du stream sont réellement traités.

Donc, vous pouvez aller avec votre approche qui est parfaitement bien.

Cependant, cela me semble inefficace, car le filtre va scanner toute la liste

Non, ce ne sera pas le cas – il sera “cassé” dès que le premier élément satisfaisant au prédicat sera trouvé. Vous pouvez en savoir plus sur la paresse dans le paquet de stream javadoc , en particulier (emphase):

De nombreuses opérations de stream, telles que le filtrage, le mappage ou la suppression des doublons, peuvent être mises en œuvre paresseusement, exposant les possibilités d’optimisation. Par exemple, “trouver la première chaîne avec trois voyelles consécutives” n’a pas besoin d’examiner toutes les chaînes d’entrée. Les opérations de stream sont divisées en opérations intermédiaires (production de stream) et opérations de terminal (production de valeur ou effet secondaire). Les opérations intermédiaires sont toujours paresseuses.

 return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().orElse(null); 

J’ai dû filtrer un seul object dans une liste d’objects. Donc j’ai utilisé ça, j’espère que ça aide.

En plus de la réponse d’ Alexis C , si vous travaillez avec une liste de tableaux, dans laquelle vous n’êtes pas sûr que l’élément recherché existe, utilisez-le.

 Integer a = list.stream() .peek(num -> System.out.println("will filter " + num)) .filter(x -> x > 5) .findFirst() .orElse(null); 

Ensuite, vous pouvez simplement vérifier si un est null .

À moins que votre liste ne soit vraiment énorme (des milliers d’éléments), l’utilisation des stream ici est tout simplement coûteuse et rend le code plus difficile à comprendre.

Remarque: Java n’est pas un langage fonctionnel (et jvm n’est pas particulièrement adapté à l’implémentation efficace des langages fonctionnels).

Beaucoup plus simple et plus efficace est (sur tous les Iterable):

 for (MyType walk : lst) if (walk > 5) { do_whatever; break; } 

Ou si vous voulez sauter l’iterator:

 for (int x=0; x 5 { do_whatever; break; } 

En fait, je me demande vraiment pourquoi certaines personnes suggèrent cette machine à stream complexe et coûteuse, même pour des choses sortingviales comme obtenir le premier élément d’un tableau. (oui: les tableaux sont toujours pris en charge dans Java8).