Пример метода Java 8 Streams FlatMap
Я проверяю предстоящий Java update
, а именно: Java 8 or JDK 8
. Да, я нетерпелив, там много нового, но есть кое-что, что я не понимаю, какой-то простой код:
final Stream<Integer>stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream.flatMap();
javadocs
public <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
Возвращает поток, состоящий из результатов замены каждого элемента этого потока содержимым отображаемого потока, созданного путем применения предоставленной функции отображения к каждому элементу. Каждый отображаемый поток закрывается после того, как его содержимое было помещено в этот поток. (Если отображаемый поток равен нулю, вместо него используется пустой поток.) Это промежуточная операция.
Я был бы признателен, если бы кто-нибудь создал несколько простых реальных примеров о flatMap
, как вы могли бы его закодировать в предыдущих версиях java Java[6,7]
и как вы можете кодировать те же подпрограммы с помощью Java 8
.
Ответы
Ответ 1
Не имеет смысла использовать flatMap
для потока, который уже плоский, например, для Stream<Integer>
вы указали в своем вопросе.
Однако, если у вас есть Stream<List<Integer>>
тогда это будет иметь смысл, и вы можете сделать это:
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);
Который напечатал бы:
1
2
3
4
5
Для этого до Java 8 вам просто нужны циклы:
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);
}
Ответ 2
Сделанный пример
Представьте, что вы хотите создать следующую последовательность: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 и т.д. (другими словами: 1x1, 2x2, 3x3 и т.д.)
С flatMap
он может выглядеть так:
IntStream sequence = IntStream.rangeClosed(1, 4)
.flatMap(i -> IntStream.iterate(i, identity()).limit(i));
sequence.forEach(System.out::println);
где:
-
IntStream.rangeClosed(1, 4)
создает поток int
от 1 до 4 включительно
-
IntStream.iterate(i, identity()).limit(i)
создает поток длины я из int
я - поэтому применяется к i = 4
, он создает поток: 4, 4, 4, 4
-
flatMap
"выравнивает" поток и "объединяет" его с исходным потоком.
С Java < 8 вам понадобятся две вложенные петли:
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 4; i++) {
for (int j = 0; j < i; j++) {
list.add(i);
}
}
Пример реального мира
Скажем, у меня есть List<TimeSeries>
, где каждый TimeSeries
по существу a Map<LocalDate, Double>
. Я хочу получить список всех дат, для которых по крайней мере один из временных рядов имеет значение. 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()
улучшит производительность, если вы не будете писать какой-либо параллельный код.
Ответ 3
Извлечь уникальные слова, отсортированные по ASC из списка фраз:
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);
... и вывод:
Unique words: [confounded, incumbent, jailer, perjury, skimming, sporadic]
Ответ 4
Я единственный, кто считает, что раскручивание списков скучно?; -)
Попробуйте использовать объекты. Кстати, пример реального мира.
Задано: Объект, представляющий повторяющуюся задачу. О важных полях задач: напоминания начинают звонить в start
и повторяются каждые repeatPeriod
repeatUnit
(например, 5 ЧАСОВ), и будет всего repeatCount
напоминаний (включая запуск).
Цель: создать список копий задания, по одному для каждого вызова напоминания о задаче.
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);
Вывод:
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}
P.S.: Я был бы признателен, если бы кто-то предложил более простое решение, я вообще не профессионал.
UPDATE:
@RBz попросил подробное объяснение, так вот оно.
В основном flatMap помещает все элементы из потоков внутри другого потока в выходной поток. Здесь много потоков:). Таким образом, для каждой задачи в исходном потоке lambda выражение x -> LongStream.iterate...
создает поток длинных значений, представляющих моменты запуска задачи. Этот поток ограничен экземплярами x.getRepeatCount()
. Значение начинается с x.getStart().toEpochSecond(ZoneOffset.UTC)
, и каждое следующее значение вычисляется с использованием lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()
. boxed()
возвращает поток с каждым длинным значением в качестве экземпляра Long wrapper. Затем каждый Long в этом потоке сопоставляется с новым экземпляром Task, который больше не повторяется и содержит точное время выполнения. Этот образец содержит только одну задачу в списке ввода. Но представьте, что у вас есть тысяча. У вас будет поток из 1000 потоков объектов Task. И то, что flatMap
делает здесь, помещает все Задачи из всех потоков в один и тот же выходной поток. Это все, как я понимаю. Спасибо за ваш вопрос!
Ответ 5
Очень простой пример: разделить список полных имен, чтобы получить список имен, независимо от имени или фамилии
List<String> 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
Ответ 6
Учитывая это:
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 + '}';
}
}
и это:
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()
{
}
}
Затем мы можем сделать это:
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 );
Ответ 7
Этот метод принимает одну функцию в качестве аргумента, эта функция принимает один параметр T в качестве входного аргумента и возвращает один поток параметра R в качестве возвращаемого значения. Когда эта функция применяется к каждому элементу этого потока, она создает поток новых значений. Все элементы этих новых потоков, генерируемые каждым элементом, затем копируются в новый поток, который будет возвратным значением этого метода.
http://codedestine.com/java-8-stream-flatmap-method/