libreria funcional examples ejemplo java java-8 java-stream flatmap

funcional - Ejemplo del método Java 8 Streams FlatMap



libreria stream java (7)

Ejemplo hecho

Imagine que desea crear la siguiente secuencia: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, etc. (en otras palabras: 1x1, 2x2, 3x3, etc.)

Con flatMap podría verse así:

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

dónde:

  • IntStream.rangeClosed(1, 4) crea una secuencia de int de 1 a 4, inclusive
  • IntStream.iterate(i, identity()).limit(i) crea una secuencia de longitud i de int i - así aplicada a i = 4 crea una secuencia: 4, 4, 4, 4
  • flatMap "aplana" la secuencia y "la concatena" con la secuencia original

Con Java <8 necesitaría dos bucles anidados:

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

Ejemplo del mundo real

Digamos que tengo un List<TimeSeries> donde cada TimeSeries es esencialmente un Map<LocalDate, Double> . Quiero obtener una lista de todas las fechas para las cuales al menos una de las series de tiempo tiene un valor. flatMap al rescate:

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

No solo es legible, sino que si de repente necesita procesar 100k elementos, simplemente al agregar parallel() mejorará el rendimiento sin que usted escriba ningún código simultáneo.

He estado revisando la próxima Java update , a saber: Java 8 or JDK 8 . Sí, soy impaciente, hay muchas cosas nuevas, pero hay algo que no entiendo, un código simple:

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

los javadocs son

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

Devuelve una secuencia que consiste en los resultados de reemplazar cada elemento de esta secuencia con los contenidos de una secuencia mapeada producida al aplicar la función de asignación proporcionada a cada elemento. Cada flujo mapeado se cierra después de que su contenido se haya colocado en esta secuencia. (Si una secuencia mapeada es nula, se usa una secuencia vacía). Esta es una operación intermedia.

Agradecería que alguien creara algunos ejemplos simples de la vida real sobre flatMap , cómo podría codificarlo en versiones anteriores de Java Java[6,7] y cómo puede codificar las mismas rutinas usando Java 8 .


¿Soy el único que descubre aburrir las listas de desenrollar? ;-)

Probemos con los objetos. Ejemplo del mundo real por cierto.

Dado: Objeto que representa una tarea repetitiva. Acerca de los campos de tareas importantes: los recordatorios comienzan a sonar al start y repiten cada repeatPeriod repeatUnit (por ejemplo, 5 HORAS) y habrá recordatorios de repeatCount en total (incluido el inicio de uno).

Objetivo: lograr una lista de copias de tareas, una para cada invocación de recordatorio de tarea.

List<Task> 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);

Salida:

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}

PD: Agradecería que alguien sugiriera una solución más simple, después de todo no soy un profesional.

ACTUALIZACIÓN: @RBz pidió una explicación detallada, así que aquí está. Básicamente, flatMap coloca todos los elementos de las corrientes dentro de otra corriente en la secuencia de salida. Muchas corrientes aquí :). Por lo tanto, para cada tarea en la secuencia inicial, la expresión lambda x -> LongStream.iterate... crea una secuencia de valores largos que representan los momentos de inicio de la tarea. Esta transmisión está limitada a las instancias x.getRepeatCount() . Sus valores comienzan desde x.getStart().toEpochSecond(ZoneOffset.UTC) y cada valor siguiente se calcula utilizando lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds() . boxed() devuelve la secuencia con cada valor largo como una instancia de envoltura larga. Luego, cada longitud de esa secuencia se asigna a una nueva instancia de tarea que ya no es repetitiva y contiene el tiempo de ejecución exacto. Este ejemplo contiene solo una tarea en la lista de entrada. imagina que tienes mil. Tendrás entonces una secuencia de 1000 flujos de objetos Task. Y lo que hace flatMap aquí es poner todas las tareas de todas las transmisiones en la misma secuencia de salida. Eso es todo lo que entiendo. Gracias por tu pregunta !


Dado este:

public class SalesTerritory { private String territoryName; private Set<String> geographicExtents; public SalesTerritory( String territoryName, Set<String> zipCodes ) { this.territoryName = territoryName; this.geographicExtents = zipCodes; } public String getTerritoryName() { return territoryName; } public void setTerritoryName( String territoryName ) { this.territoryName = territoryName; } public Set<String> getGeographicExtents() { return geographicExtents != null ? Collections.unmodifiableSet( geographicExtents ) : Collections.emptySet(); } public void setGeographicExtents( Set<String> 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 + ''}''; } }

y esto:

public class SalesTerritories { private static final Set<SalesTerritory> territories = new HashSet<>( Arrays.asList( new SalesTerritory[]{ new SalesTerritory( "North-East, USA", new HashSet<>( Arrays.asList( new String[]{ "Maine", "New Hampshire", "Vermont", "Rhode Island", "Massachusetts", "Connecticut", "New York", "New Jersey", "Delaware", "Maryland", "Eastern Pennsylvania", "District 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 String[]{ "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 String[]{ "Oklahoma", "Kansas", "Nebraska", "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 String[]{ "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 String[]{ "California", "Hawaii" } ) ) ) } ) ); public static Set<SalesTerritory> getAllTerritories() { return Collections.unmodifiableSet( territories ); } private SalesTerritories() { } }

Entonces podemos hacer esto:

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 -> String.format( "%-30s : %s", e.getKey(), e.getValue() ) ) .forEach( System.out::println );


Este método toma una función como argumento, esta función acepta un parámetro T como argumento de entrada y devuelve una secuencia del parámetro R como valor de retorno. Cuando esta función se aplica a cada elemento de esta secuencia, produce una secuencia de nuevos valores. Todos los elementos de estas nuevas transmisiones generadas por cada elemento se copian en una nueva secuencia, que será un valor de retorno de este método.

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


Extraiga palabras únicas ordenadas ASC de una lista de frases:

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

... y el resultado:

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


No tiene sentido flatMap un Stream que ya es plano, como el Stream<Integer> que has mostrado en tu pregunta.

Sin embargo, si tuviera un Stream<List<Integer>> entonces tendría sentido y podría hacer esto:

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

Que imprimiría:

1 2 3 4 5

Para hacer esto pre-Java 8 solo necesitas un bucle:

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


Un ejemplo muy simple: dividir una lista de nombres completos para obtener una lista de nombres, independientemente de la primera o la última

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

Esto se imprime:

Barry Allen Bruce Wayne Clark Kent