88

我一直在检查即将到来的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.

4

7 回答 7

161

对于已经平坦flatMapStream没有意义,就像Stream<Integer>您在问题中显示的那样。

但是,如果你有一个Stream<List<Integer>>then 它是有意义的,你可以这样做:

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);
}
于 2014-03-13T15:58:33.680 回答
117

编造的例子

假设您要创建以下序列: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)创建一个长度为 i 的流inti - 因此应用于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>where eachTimeSeries本质上是一个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());

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

于 2014-03-13T15:57:51.637 回答
21

从短语列表中提取按 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("\\s+")))
        .distinct()
        .sorted()
        .collect(Collectors.toList());
System.out.println("Unique words: " + uniqueWords);

...和输出:

Unique words: [confounded, incumbent, jailer, perjury, skimming, sporadic]
于 2015-02-09T03:39:28.477 回答
11

我是唯一一个觉得展开列表很无聊的人吗?;-)

让我们尝试使用对象。顺便说一句,现实世界的例子。

给定:表示重复性任务的对象。关于重要任务领域:提醒开始响起start并每隔(例如5小时)重复一次,总共repeatPeriod repeatUnit会有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:如果有人提出更简单的解决方案,我将不胜感激,毕竟我不是专业人士。

更新: @RBz 要求详细解释,所以在这里。基本上 flatMap 将流中的所有元素放入另一个流中的输出流中。这里有很多流:)。因此,对于初始流中的每个任务,lambda 表达式x -> LongStream.iterate...都会创建一个表示任务开始时刻的长值流。此流仅限于x.getRepeatCount()实例。它的值从 开始x.getStart().toEpochSecond(ZoneOffset.UTC),每个下一个值都是使用 lambda 计算的p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()boxed()返回具有每个 long 值的流作为 Long 包装器实例。然后,该流中的每个 Long 都映射到不再重复且包含准确执行时间的新 Task 实例。此示例在输入列表中仅包含一个任务。但是想象一下,你有一千个。然后,您将拥有 1000 个 Task 对象流。还有什么flatMap这里所做的是将所有流中的所有任务放到同一个输出流上。这就是我所理解的。谢谢你的问题!

于 2014-09-29T18:40:13.487 回答
2

该方法接受一个函数作为参数,该函数接受一个参数 T 作为输入参数,并返回一个参数流 R 作为返回值。当此函数应用于此流的每个元素时,它会生成一个新值流。然后将每个元素生成的这些新流的所有元素复制到一个新流中,这将是该方法的返回值。

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

于 2017-02-10T10:41:25.707 回答
2

一个非常简单的例子:拆分一个全名列表得到一个名字列表,不分姓氏和名字

 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
于 2017-11-16T17:41:12.687 回答
1

鉴于这种:

  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 );
于 2016-11-05T01:11:34.097 回答