简体   繁体   English

Java 8 Streams FlatMap 方法示例

[英]Java 8 Streams FlatMap method example

I have been checking the upcoming Java update , namely: Java 8 or JDK 8 .我一直在检查即将到来的Java update ,即: 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:是的,我很不耐烦,有很多新东西,但是,有一些我不明白,一些简单的代码:

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. (如果映射流为空,则使用空流代替。)这是一个中间操作。

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编写相同的例程,我将不胜感激。

It doesn't make sense to flatMap a Stream that's already flat, like the Stream<Integer> you've shown in your question. flatMap一个已经平坦的Stream是没有意义的,就像你在问题中显示的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);
}

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 flatMap it 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 int from 1 to 4, inclusive IntStream.rangeClosed(1, 4)创建一个从 1 到 4 的int流,包括
  • IntStream.iterate(i, identity()).limit(i) creates a stream of length i of int i - so applied to i = 4 it creates a stream: 4, 4, 4, 4 IntStream.iterate(i, identity()).limit(i)创建一个长度为 i 的int i 的流 - 所以应用于i = 4它创建了一个流: 4, 4, 4, 4
  • flatMap "flattens" the stream and "concatenates" it to the original stream 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 TimeSeries is essentially a Map<LocalDate, Double> .假设我有一个List<TimeSeries> ,其中每个TimeSeries本质上是一个Map<LocalDate, Double> I want to get a list of all dates for which at least one of the time series has a value.我想获取至少一个时间序列具有值的所有日期的列表。 flatMap to the rescue: 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.它不仅可读,而且如果您突然需要处理 100k 个元素,只需添加parallel()提高性能,而无需编写任何并发代码。

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]

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 start and repeat every repeatPeriod repeatUnit (eg 5 HOURS) and there will be repeatCount reminders in total(including starting one).关于重要的任务字段:提醒开始时start响铃,每repeatPeriod repeatUnit (例如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}

PS: 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.更新: @RBz 要求详细解释,所以在这里。 Basically flatMap puts all elements from streams inside another stream into output stream.基本上 flatMap 将来自另一个流中的流的所有元素放入输出流中。 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.因此,对于初始流 lambda 表达式中的每个任务x -> LongStream.iterate...创建一个表示任务开始时刻的长值流。 This stream is limited to x.getRepeatCount() instances.此流仅限于x.getRepeatCount()实例。 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 flatMap does 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!它的值从x.getStart().toEpochSecond(ZoneOffset.UTC) ,每个下一个值都是使用 lambda p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()boxed()返回每个 long 值作为 Long 包装器实例的流。然后该流中的每个 Long 都映射到新的不再重复并包含确切执行时间的 Task 实例。此示例仅包含输入列表中的一个 Task。但是想象一下你有一千个。然后你将有一个包含 1000 个 Task 对象流的流。而flatMap在这里所做的就是将所有流中的所有任务放到同一个输出流中。这就是我的理解。谢谢你的问题!

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.该方法以一个Function作为参数,该函数接受一个参数T作为输入参数,并返回一个参数流R作为返回值。 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.然后将每个元素生成的这些新流的所有元素复制到一个新流中,这将是此方法的返回值。

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

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM