Пример метода Java 8 Streams FlatMap

Я проверяю предстоящее Java update , а именно: Java 8 or JDK 8 . Да, я нетерпелив, есть много нового, но есть кое-что, что я не понимаю, какой-то простой код:

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

javadocs –

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

Возвращает stream, состоящий из результатов замены каждого элемента этого streamа содержимым отображаемого streamа, созданного путем применения предоставленной функции отображения к каждому элементу. Каждый отображаемый stream закрывается после того, как его содержимое было помещено в этот stream. (Если отображаемый stream равен нулю, вместо него используется пустой stream.) ​​Это промежуточная операция.

Я был бы признателен, если бы кто-нибудь создал несколько простых реальных примеров о flatMap , как вы могли бы закодировать его в предыдущих версиях Java[6,7] и как вы можете кодировать одни и те же подпрограммы с помощью Java 8 .

Для flatMap не имеет смысла использовать Stream, который уже является плоским, например Stream вы указали в своем вопросе.

Однако, если у вас есть Stream> тогда это будет иметь смысл, и вы можете сделать это:

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

Что бы напечатать:

 1 2 3 4 5 

Для этого pre-Java 8 вам понадобятся циклы:

 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); } 

Приведен пример

Представьте, что вы хотите создать следующую последовательность: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 и т. Д. (Другими словами: 1×1, 2×2, 3×3 и т. Д.).

С flatMap это может выглядеть так:

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

где:

  • IntStream.rangeClosed(1, 4) создает stream int от 1 до 4 включительно
  • IntStream.iterate(i, identity()).limit(i) создает stream длины i из int i, поэтому применяется к i = 4 он создает stream: 4, 4, 4, 4
  • flatMap «выравнивает» stream и «объединяет» его с исходным streamом

С Java <8 вам понадобятся два вложенных цикла:

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

Пример реального мира

Допустим, у меня есть List где каждый TimeSeries по существу является Map . Я хочу получить список всех дат, для которых по крайней мере один из временных рядов имеет значение. flatMap на помощь:

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

Это не только читаемо, но если вам вдруг понадобится обработать 100 тыс. Элементов, просто добавление параметра parallel() улучшит производительность, если вы не будете писать какой-либо параллельный код.

Извлечь уникальные слова, отсортированные по ASC из списка фраз:

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

… и выход:

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

Я единственный, кто считает, что раскручивание списков скучно? 😉

Попробуем с объектами. Кстати, пример реального мира.

Задано: Объект, представляющий повторяющуюся задачу. О важных полях задач: напоминания начинают звонить в start и повторяются каждый повтор повторяющегося repeatUnit (например, 5 ЧАСОВ), и будут напоминания repeatCount в целом (включая начальную).

objective: получить список копий задач, по одному для каждого вызова напоминания о задаче.

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

Вывод:

 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: Я был бы признателен, если бы кто-то предложил более простое решение, я ведь не профессионал.

ОБНОВЛЕНИЕ: @RBz попросил подробное объяснение, так что вот оно. В основном flatMap помещает все элементы из streamов внутри другого streamа в выходной stream. Здесь много streamов :). Таким образом, для каждой задачи в исходном выражении lambda streamа x -> LongStream.iterate... создает stream длинных значений, которые представляют моменты запуска задачи. Этот stream ограничен x.getRepeatCount() . Это значения начинаются с x.getStart().toEpochSecond(ZoneOffset.UTC) и каждое следующее значение вычисляется с использованием lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds() . boxed() возвращает stream с каждым длинным значением в качестве экземпляра Long wrapper. Затем каждый Long в этом streamе сопоставляется с новым экземпляром Task, который больше не повторяется и содержит точное время выполнения. Этот образец содержит только одну задачу во входном списке. представьте, что у вас есть flatMap у вас будет stream из 1000 streamов объектов flatMap что такое flatMap делает здесь все задачи из всех streamов в один выходной flatMap все, как я понимаю. Спасибо за ваш вопрос !

Очень простой пример: разделите список полных имен, чтобы получить список имен, независимо от первого или последнего

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

Это печатает:

 Barry Allen Bruce Wayne Clark Kent 

Учитывая это:

  public class SalesTerritory { private String territoryName; private Set geographicExtents; public SalesTerritory( String territoryName, Set zipCodes ) { this.territoryName = territoryName; this.geographicExtents = zipCodes; } public String getTerritoryName() { return territoryName; } public void setTerritoryName( String 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 + '}'; } } 

и это:

 public class SalesTerritories { private static final Set 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 getAllTerritories() { return Collections.unmodifiableSet( territories ); } private SalesTerritories() { } } 

Затем мы можем сделать следующее:

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

Этот метод принимает одну функцию в качестве аргумента, эта функция принимает один параметр T в качестве входного аргумента и возвращает один stream параметра R в качестве возвращаемого значения. Когда эта функция применяется к каждому элементу этого streamа, она создает stream новых значений. Все элементы этих новых streamов, генерируемые каждым элементом, затем копируются в новый stream, который будет возвратным значением этого метода.

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

  • Как создать stream из массива?
  • Итерация двух Java-8-streamов вместе
  • В Java 8 есть class ByteStream?
  • Java 8: производительность streamов и коллекций
  • Использовать ссылку на метод с параметром
  • Как преобразовать stream Java 8 в массив?
  • Почему java.util.Collection не реализует новый интерфейс Stream?
  • Найти первый элемент по предикату
  • В streamах Java просматривается действительно только для отладки?
  • Есть ли сжатый способ перебора streamа с индексами в Java 8?
  • Использование опций Java 8 с Stream :: flatMap
  • Давайте будем гением компьютера.