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)

返回一个stream,该stream包含将此stream的每个元素replace为通过将所提供的映射函数应用于每个元素而生成的映射stream的内容的结果。 每个映射stream在其内容放入此stream之后都会closures。 (如果映射stream为空,则使用空stream。)这是一个中间操作。

如果有人为flatMap创build了一些简单的现实生活中的例子,如何在之前的java版本中编写Java[6,7]以及如何使用Java 8编写相同的例程,我将不胜感激。

扁平化一个已经平坦的Stream<Integer> ,比如你在问题中显示的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); } 

编写例子

想象一下,你想创build以下序列:1,2,3,2,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)创build一个从1到4(含IntStream.rangeClosed(1, 4)intstream
  • IntStream.iterate(i, identity()).limit(i)创build一个int i的长度为i的stream – 所以应用于i = 4它创build一个stream: IntStream.iterate(i, identity()).limit(i)
  • flatMap “平整”stream,并将其“连接”到原始stream

使用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本质上是一个Map<LocalDate, Double> 。 我想获得至less一个时间序列具有值的所有date的列表。 flatMap来拯救:

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

它不仅可读,而且如果突然需要处理100k个元素,只需添加parallel()就可以提高性能,而无需编写任何并发代码。

从短语列表中提取独特的单词sortingASC:

 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] 

我是唯一一个发现无聊的名单无聊吗? 😉

让我们尝试与对象。 真实世界的例子。

鉴于:代表重复任务的对象。 关于重要任务字段:提醒开始响起,并重复每个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} 

PS:如果有人提出了一个更简单的解决scheme,我将不胜感激,毕竟我不是专业人士。

更新: @RBz要求详细的解释,所以在这里。 基本上,flatMap将来自另一个stream内的stream的所有元素放入输出stream。 很多stream在这里:)。 因此,对于初始streamlambdaexpression式中的每个任务x -> LongStream.iterate...创build一个表示任务启动时刻的长x -> LongStream.iterate...值stream。 这个stream限于x.getRepeatCount()实例。 它的值从x.getStart().toEpochSecond(ZoneOffset.UTC) ,每个下一个值使用lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()boxed()将每个long值的stream作为一个Long包装器实例返回,然后将该stream中的每个Long映射到不再重复的新的Task实例,并且包含确切的执行时间,该示例只包含input列表中的一个Task。想象一下,你有一千个,然后你将有一个1000stream的任务对象的stream,而flatMap在这里做的是把所有stream的所有任务都放到同一个输出stream中,这就是我所理解的。 !

鉴于这种:

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

一个非常简单的例子:分割一个全名列表来获取名字列表,而不pipe第一个还是最后一个

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

这打印出来:

 Barry Allen Bruce Wayne Clark Kent 

该方法接受一个Function作为参数,该函数接受一个参数T作为input参数,并返回一个参数R的stream作为返回值。 当这个函数应用于这个stream的每个元素时,它会产生一个新的值stream。 所有这些由每个元素生成的新stream的元素都被复制到一个新的stream中,这将是这个方法的返回值。

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