Exemple de méthode Java 8 Streams FlatMap

J’ai vérifié la prochaine Java update , à savoir: Java 8 or JDK 8 . Oui, je suis impatient, il y a beaucoup de nouveautés, mais il y a quelque chose que je ne comprends pas, du code simple:

 final Streamstream = Stream.of(1,2,3,4,5,6,7,8,9,10); stream.flatMap(); 

les javadocs sont

public Stream flatMap(Function<? super T,? extends Stream> mapper)

Renvoie un stream contenant les résultats du remplacement de chaque élément de ce stream par le contenu d’un stream mappé produit en appliquant la fonction de mappage fournie à chaque élément. Chaque stream mappé est fermé après que son contenu ait été placé dans ce stream. (Si un stream mappé est nul, un stream vide est utilisé à la place.) Il s’agit d’une opération intermédiaire.

J’apprécierais que quelqu’un crée des exemples flatMap de flatMap , comment vous pourriez le coder dans les versions précédentes de Java[6,7] et comment vous pouvez coder les mêmes routines en utilisant Java 8 .

Cela n’a aucun sens de flatMap un Stream qui est déjà plat, comme le Stream vous avez montré dans votre question.

Cependant, si vous aviez un Stream> cela aurait du sens et vous pourriez le faire:

 Stream> integerListStream = Stream.of( Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5) ); Stream integerStream = integerListStream .flatMap(Collection::stream); integerStream.forEach(System.out::println); 

Qui imprimerait:

 1 2 3 4 5 

Pour faire cette pré-Java 8, il vous suffit de créer une boucle:

 List> integerLists = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5) ) List flattened = new ArrayList<>(); for (List integerList : integerLists) { flattened.addAll(integerList); } for (Integer i : flattened) { System.out.println(i); } 

Exemple composé

Imaginez que vous vouliez créer la séquence suivante: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 etc. (en d’autres termes: 1×1, 2×2, 3×3, etc.)

Avec flatMap cela pourrait ressembler à:

 IntStream sequence = IntStream.rangeClosed(1, 4) .flatMap(i -> IntStream.iterate(i, identity()).limit(i)); sequence.forEach(System.out::println); 

où:

  • IntStream.rangeClosed(1, 4) crée un stream d’ int de 1 à 4 inclus
  • IntStream.iterate(i, identity()).limit(i) crée un stream de longueur i de int i – donc appliqué à i = 4 il crée un stream: 4, 4, 4, 4
  • flatMap “aplatit” le stream et le “concatène” au stream d’origine

Avec Java <8, vous auriez besoin de deux boucles imbriquées:

 List list = new ArrayList<>(); for (int i = 1; i <= 4; i++) { for (int j = 0; j < i; j++) { list.add(i); } } 

Exemple du monde réel

Disons que j'ai une List où chaque TimeSeries est essentiellement une Map . Je veux obtenir une liste de toutes les dates pour lesquelles au moins une des séries temporelles a une valeur. flatMap à la rescousse:

 list.stream().parallel() .flatMap(ts -> ts.dates().stream()) // for each TS, stream dates and flatmap .distinct() // remove duplicates .sorted() // sort ascending .collect(toList()); 

Non seulement est-il lisible, mais si vous devez soudainement traiter 100 000 éléments, append simplement parallel() améliorera les performances sans écrire de code simultané.

Extrayez des mots uniques classés ASC parmi une liste de phrases:

 List phrases = Arrays.asList( "sporadic perjury", "confounded skimming", "incumbent jailer", "confounded jailer"); List uniqueWords = phrases .stream() .flatMap(phrase -> Stream.of(phrase.split(" +"))) .distinct() .sorted() .collect(Collectors.toList()); System.out.println("Unique words: " + uniqueWords); 

… et la sortie:

 Unique words: [confounded, incumbent, jailer, perjury, skimming, sporadic] 

Suis-je le seul qui trouve les listes de déroulement ennuyeuses? 😉

Essayons avec des objects. Un exemple concret au fait.

Compte tenu de: Objet représentant une tâche répétitive. À propos des champs de tâches importants: les rappels commencent à sonner au start et répètent chaque repeatPeriod repeatUnit (par exemple, 5 HEURES) et il y aura des rappels de la repeatCount (y compris un rappel).

Objectif: obtenir une liste de copies de tâches, une pour chaque appel de rappel de tâche.

 List tasks = Arrays.asList( new Task( false,//completed sign "My important task",//task name (text) LocalDateTime.now().plus(2, ChronoUnit.DAYS),//first reminder(start) true,//is task repetitive? 1,//reminder interval ChronoUnit.DAYS,//interval unit 5//total number of reminders ) ); tasks.stream().flatMap( x -> LongStream.iterate( x.getStart().toEpochSecond(ZoneOffset.UTC), p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()) ).limit(x.getRepeatCount()).boxed() .map( y -> new Task(x,LocalDateTime.ofEpochSecond(y,0,ZoneOffset.UTC))) ).forEach(System.out::println); 

Sortie:

 Task{completed=false, text='My important task', start=2014-10-01T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null} Task{completed=false, text='My important task', start=2014-10-02T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null} Task{completed=false, text='My important task', start=2014-10-03T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null} Task{completed=false, text='My important task', start=2014-10-04T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null} Task{completed=false, text='My important task', start=2014-10-05T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null} 

PS: J’apprécierais que quelqu’un propose une solution plus simple, je ne suis pas un pro après tout.

MISE À JOUR: @RBz a demandé une explication détaillée alors la voici. FlatMap place tous les éléments des stream dans un autre stream dans le stream de sortie. Beaucoup de stream ici :). Ainsi, pour chaque tâche du stream initial, l’expression lambda x -> LongStream.iterate... crée un stream de valeurs longues représentant les moments de début de la tâche. Ce stream est limité aux x.getRepeatCount() de x.getRepeatCount() . x.getStart().toEpochSecond(ZoneOffset.UTC) valeurs commencent par x.getStart().toEpochSecond(ZoneOffset.UTC) et chaque valeur suivante est calculée à l’aide de lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds() . boxed() renvoie le stream avec chaque valeur longue en tant qu’instance de wrapper Long. Chaque object Long de ce stream est alors mappé sur la nouvelle instance de tâche qui n’est plus répétitive et contient la durée d’exécution exacte. Imaginez que vous avez un millier. Vous aurez alors un stream de 1000 stream d’objects Task. Et ce que flatMap fait ici, c’est de placer toutes les tâches de tous les stream sur le même stream de sortie. !

Un exemple très simple: divisez une liste de noms complets pour obtenir une liste de noms, indépendamment du premier ou du dernier

  List fullNames = Arrays.asList("Barry Allen", "Bruce Wayne", "Clark Kent"); fullNames.stream() .flatMap(fullName -> Pattern.comstack(" ").splitAsStream(fullName)) .forEach(System.out::println); 

Cela imprime:

 Barry Allen Bruce Wayne Clark Kent 

Compte tenu de ceci:

  public class SalesTerritory { private Ssortingng territoryName; private Set geographicExtents; public SalesTerritory( Ssortingng territoryName, Set zipCodes ) { this.territoryName = territoryName; this.geographicExtents = zipCodes; } public Ssortingng getTerritoryName() { return territoryName; } public void setTerritoryName( Ssortingng territoryName ) { this.territoryName = territoryName; } public Set getGeographicExtents() { return geographicExtents != null ? Collections.unmodifiableSet( geographicExtents ) : Collections.emptySet(); } public void setGeographicExtents( Set geographicExtents ) { this.geographicExtents = new HashSet<>( geographicExtents ); } @Override public int hashCode() { int hash = 7; hash = 53 * hash + Objects.hashCode( this.territoryName ); return hash; } @Override public boolean equals( Object obj ) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( getClass() != obj.getClass() ) { return false; } final SalesTerritory other = (SalesTerritory) obj; if ( !Objects.equals( this.territoryName, other.territoryName ) ) { return false; } return true; } @Override public String toString() { return "SalesTerritory{" + "territoryName=" + territoryName + ", geographicExtents=" + geographicExtents + '}'; } } 

et ça:

 public class SalesTerritories { private static final Set territories = new HashSet<>( Arrays.asList( new SalesTerritory[]{ new SalesTerritory( "North-East, USA", new HashSet<>( Arrays.asList( new Ssortingng[]{ "Maine", "New Hampshire", "Vermont", "Rhode Island", "Massachusetts", "Connecticut", "New York", "New Jersey", "Delaware", "Maryland", "Eastern Pennsylvania", "Dissortingct of Columbia" } ) ) ), new SalesTerritory( "Appalachia, USA", new HashSet<>( Arrays.asList( new String[]{ "West-Virgina", "Kentucky", "Western Pennsylvania" } ) ) ), new SalesTerritory( "South-East, USA", new HashSet<>( Arrays.asList( new Ssortingng[]{ "Virginia", "North Carolina", "South Carolina", "Georgia", "Florida", "Alabama", "Tennessee", "Mississippi", "Arkansas", "Louisiana" } ) ) ), new SalesTerritory( "Mid-West, USA", new HashSet<>( Arrays.asList( new String[]{ "Ohio", "Michigan", "Wisconsin", "Minnesota", "Iowa", "Missouri", "Illinois", "Indiana" } ) ) ), new SalesTerritory( "Great Plains, USA", new HashSet<>( Arrays.asList( new Ssortingng[]{ "Oklahoma", "Kansas", "Nearmka", "South Dakota", "North Dakota", "Eastern Montana", "Wyoming", "Colorada" } ) ) ), new SalesTerritory( "Rocky Mountain, USA", new HashSet<>( Arrays.asList( new String[]{ "Western Montana", "Idaho", "Utah", "Nevada" } ) ) ), new SalesTerritory( "South-West, USA", new HashSet<>( Arrays.asList( new Ssortingng[]{ "Arizona", "New Mexico", "Texas" } ) ) ), new SalesTerritory( "Pacific North-West, USA", new HashSet<>( Arrays.asList( new String[]{ "Washington", "Oregon", "Alaska" } ) ) ), new SalesTerritory( "Pacific South-West, USA", new HashSet<>( Arrays.asList( new Ssortingng[]{ "California", "Hawaii" } ) ) ) } ) ); public static Set getAllTerritories() { return Collections.unmodifiableSet( territories ); } private SalesTerritories() { } } 

Nous pouvons alors faire ceci:

 System.out.println(); System.out .println( "We can use 'flatMap' in combination with the 'AbstractMap.SimpleEntry' class to flatten a hierarchical data-structure to a set of Key/Value pairs..." ); SalesTerritories.getAllTerritories() .stream() .flatMap( t -> t.getGeographicExtents() .stream() .map( ge -> new SimpleEntry<>( t.getTerritoryName(), ge ) ) ) .map( e -> Ssortingng.format( "%-30s : %s", e.getKey(), e.getValue() ) ) .forEach( System.out::println ); 

Cette méthode prend une fonction comme argument, cette fonction accepte un paramètre T comme argument d’entrée et renvoie un stream du paramètre R comme valeur de retour. Lorsque cette fonction est appliquée à chaque élément de ce stream, elle génère un stream de nouvelles valeurs. Tous les éléments de ces nouveaux stream générés par chaque élément sont ensuite copiés dans un nouveau stream, qui sera une valeur de retour de cette méthode.

http://codedestine.com/java-8-stream-flatmap-method/