Java 8 Streams FlatMap方法示例

时间:2014-03-13 14:53:55

标签: java java-8 java-stream 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编写相同的例程,我将不胜感激。

7 个答案:

答案 0 :(得分:153)

对于flatMap Stream已经持平的问题没有意义,例如您在问题中显示的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);
}

答案 1 :(得分:109)

编写示例

想象一下,你想要创建以下序列:1,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)从1到4创建int的流,包括
  • IntStream.iterate(i, identity()).limit(i)创建了int i的长度为i的流 - 因此应用于i = 4它会创建一个流:4, 4, 4, 4
  • flatMap“展平”流并将其“连接”到原始流

使用Java&lt; 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>。我想获得至少有一个时间序列具有值的所有日期的列表。 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()即可在不编写任何并发代码的情况下提高性能。

答案 2 :(得分:16)

从短语列表中提取对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]

答案 3 :(得分:11)

我是唯一一个发现解散名单无聊的人吗? ; - )

让我们试试对象吧。顺便说一下现实世界的例子。

给定:表示重复任务的对象。关于重要任务字段:提醒已开始响铃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。:如果有人提出更简单的解决方案,我将不胜感激,毕竟我不是专业人士。

<强>更新 @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在这里做的是将所有流中的所有任务放在同一输出流上。这完全符合我的理解。谢谢你的提问!

答案 4 :(得分: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

答案 5 :(得分:1)

鉴于此:

for

和此:

string a = new string('a', 11).Replace("aa", "ab");

string b = string.Concat(Enumerable.Range(0, 11).Select(i => "ab"[i & 1]));

string c = new StringBuilder().Insert(0, "ab", ((11 + 1) / 2)).ToString(0, 11);

我们可以这样做:

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

    }

答案 6 :(得分:1)

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

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