Java 8 Streams FlatMap 方法示例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22382453/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:25:12  来源:igfitidea点击:

Java 8 Streams FlatMap method example

javajava-8java-streamflatmap

提问by chiperortiz

I have been checking the upcoming Java update, namely: Java 8 or JDK 8. Yes, I am impatient, there's a lot of new stuff, but, there is something I don't understand, some simple code:

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

the javadocs are

javadocs 是

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

Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.) This is an intermediate operation.

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

返回一个流,该流由通过将提供的映射函数应用于每个元素而生成的映射流的内容替换此流的每个元素的结果组成。每个映射流在其内容放入该流后关闭。(如果映射流为空,则使用空流代替。)这是一个中间操作。

I would appreciate if somebody created some simple real-life examples about flatMap, how you could code it in previous java versions Java[6,7]and how you can code the same routines using Java 8.

如果有人创造了约一些简单的现实生活中的例子我将不胜感激flatMap,你怎么可以在以前的Java版本是代码Java[6,7]以及如何使用代码相同的程序Java 8

采纳答案by Nick Holt

It doesn't make sense to flatMapa Streamthat's already flat, like the Stream<Integer>you've shown in your question.

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

However, if you had a Stream<List<Integer>>then it would make sense and you could do this:

但是,如果你有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);

Which would print:

哪个会打印:

1
2
3
4
5

To do this pre-Java 8 you just need a loops:

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

回答by assylias

Made up example

编造的例子

Imagine that you want to create the following sequence: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 etc. (in other words: 1x1, 2x2, 3x3 etc.)

假设您要创建以下序列:1、2、2、3、3、3、4、4、4、4 等(换句话说:1x1、2x2、3x3 等)

With flatMapit could look like:

有了flatMap它可能看起来像:

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

where:

在哪里:

  • IntStream.rangeClosed(1, 4)creates a stream of intfrom 1 to 4, inclusive
  • IntStream.iterate(i, identity()).limit(i)creates a stream of length i of inti - so applied to i = 4it creates a stream: 4, 4, 4, 4
  • flatMap"flattens" the stream and "concatenates" it to the original stream
  • IntStream.rangeClosed(1, 4)创建int从 1 到 4的流,包括
  • IntStream.iterate(i, identity()).limit(i)创建一个长度为inti的流i - 因此应用于i = 4它会创建一个流:4, 4, 4, 4
  • flatMap“展平”流并将其“连接”到原始流

With Java < 8 you would need two nested loops:

使用 Java < 8 你需要两个嵌套循环:

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

Real world example

真实世界的例子

Let's say I have a List<TimeSeries>where each TimeSeriesis essentially a Map<LocalDate, Double>. I want to get a list of all dates for which at least one of the time series has a value. flatMapto the rescue:

假设我有一个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());

Not only is it readable, but if you suddenly need to process 100k elements, simply adding parallel()will improve performance without you writing any concurrent code.

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

回答by Aleksandr Kravets

Am I the only one who finds unwinding lists boring? ;-)

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

Let's try with objects. Real world example by the way.

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

Given: Object representing repetitive task. About important task fields: reminders are starting to ring at startand repeat every repeatPeriodrepeatUnit(e.g. 5 HOURS) and there will be repeatCountreminders in total(including starting one).

给定:代表重复性任务的对象。关于重要任务字段:提醒开始响铃start并每隔repeatPeriodrepeatUnit(例如5小时)重复一次repeatCount,总共会有提醒(包括开始)。

Goal: achieve a list of task copies, one for each task reminder invocation.

目标:实现任务副本列表,每个任务提醒调用一个。

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

Output:

输出:

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.: I would appreciate if someone suggested a simpler solution, I'm not a pro after all.

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

UPDATE:@RBz asked for detailed explanation so here it is. Basically flatMap puts all elements from streams inside another stream into output stream. A lot of streams here :). So, for each Task in initial stream lambda expression x -> LongStream.iterate...creates a stream of long values that represent task start moments. This stream is limited to x.getRepeatCount()instances. It's values start from x.getStart().toEpochSecond(ZoneOffset.UTC)and each next value is calculated using lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds(). boxed()returns the stream with each long value as a Long wrapper instance. Then each Long in that stream is mapped to new Task instance that is not repetitive anymore and contains exact execution time. This sample contains only one Task in input list. But imagine that you have a thousand. You will have then a stream of 1000 streams of Task objects. And what flatMapdoes here is putting all Tasks from all streams onto the same output stream. That's all as I understand it. Thank you for your question!

更新:@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这里所做的是将所有流中的所有任务放到同一个输出流上。这就是我的理解。谢谢你的问题!

回答by Igor Baiborodine

Extract unique words sorted ASC from a list of phrases:

从短语列表中提取按 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);

... and the output:

...和输出:

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

回答by G Butler

Given this:

鉴于这种:

  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 + '}';
        }

    }

and this:

和这个:

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()
    {
    }

}

We can then do this:

然后我们可以这样做:

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

回答by lalitbhagtani

This method takes one Function as an argument, this function accepts one parameter T as an input argument and return one stream of parameter R as a return value. When this function is applied on each element of this stream, it produces a stream of new values. All the elements of these new streams generated by each element are then copied to a new stream, which will be a return value of this method.

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

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

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

回答by Somaiah Kumbera

A very simple example: Split a list of full names to get a list of names, regardless of first or last

一个非常简单的例子:拆分一个全名列表来得到一个名字列表,不管是第一个还是最后一个

 List<String> fullNames = Arrays.asList("Barry Allen", "Bruce Wayne", "Clark Kent");

 fullNames.stream()
            .flatMap(fullName -> Pattern.compile(" ").splitAsStream(fullName))
            .forEach(System.out::println);

This prints out:

这打印出来:

Barry
Allen
Bruce
Wayne
Clark
Kent