Java 8 Streams FlatMap 메서드 예제


85

나는 다가오는 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)

이 스트림의 각 요소를 제공된 매핑 함수를 각 요소에 적용하여 생성 된 매핑 된 스트림의 내용으로 대체 한 결과로 구성된 스트림을 반환합니다. 매핑 된 각 스트림은 해당 콘텐츠가이 스트림에 배치 된 후 닫힙니다. (매핑 된 스트림이 null이면 대신 빈 스트림이 사용됩니다.) 이것은 중간 작업입니다.

누군가에 대한 간단한 실제 예제를 flatMap작성했다면 이전 Java 버전에서 코드를 작성하는 Java[6,7]방법 및 .NET을 사용하여 동일한 루틴을 코딩하는 방법 에 대해 감사하겠습니다 Java 8.


2
인터넷에서 flatMap (최소한 Scala의 경우, 기본적으로 동일합니다 :))을 사용하는 약 백만 가지의 예가 있습니다. 검색해 보셨습니까? 여기에서 시작하는 것이 있습니다 : brunton-spall.co.uk/post/2011/12/02/…
Peter Svensson

3
나는 스칼라를 이해하지 못합니다. 저는 스칼라와 함께 일한 적이 없습니다
chiperortiz

내 말은 flatMap은 현재 Java와 Scala에 존재하는 일반적인 개념이라는 것입니다.
Peter Svensson

좋아, 나는 그것에 대해 더 많이 읽을 것이다. 감사합니다.
chiperortiz

10
Java의 flatMap은 동일한 아이디어이지만 스트림과는 상당히 다릅니다. 사람들에게 스칼라를 가리 키지 마십시오!
orbfish

답변:


158

질문에 표시된 것과 같이 이미 평평한 스트림flatMap 에는 의미가 없습니다 . 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);
}

113

구성 예

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)int1에서 4까지 의 스트림을 만듭니다.
  • IntStream.iterate(i, identity()).limit(i)길이 i의 스트림을 생성 int하므로 적용 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>각 어디 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());

읽을 수있을뿐만 아니라 갑자기 10 만 개의 요소를 처리해야하는 경우 추가 만하면 parallel()동시 코드를 작성하지 않고도 성능이 향상됩니다.


14
두 가지 예 모두 허용되는 답변보다 훨씬 낫습니다.
Sebastian Graf

컴파일러가 identity ()에 대해 정의되지 않음을
Nirmal

2
@ user3320018 정적 가져 오기가 필요합니다 Function.identity.
assylias

@assylias 가져 오기 java.util.function.Function을 시도했지만 작동하지 않았습니다. 저는 Java 8을 처음 사용하며 이것은 Java 8 특정 일 수도 있고 아닐 수도 있지만 해당 오류를 제거하는 방법을 정확히 알려 주실 수 있습니다.
Nirmal

4
import static java.util.function.Function.identity;
assylias

18

구문 목록에서 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]

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}

추신 : 누군가가 더 간단한 해결책을 제안한다면 감사하겠습니다. 결국 저는 프로가 아닙니다.

업데이트 : @RBz가 자세한 설명을 요청했기 때문에 여기에 있습니다. 기본적으로 flatMap은 다른 스트림 내부의 스트림의 모든 요소를 ​​출력 스트림에 넣습니다. 여기에 많은 스트림이 있습니다 :). 따라서 초기 스트림 람다 식의 각 Task에 대해 작업 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여기서하는 일은 모든 스트림의 모든 태스크를 동일한 출력 스트림에 배치하는 것입니다. 그게 내가 이해하는 전부입니다. 질문 해주셔서 감사합니다!


8
Am I the only one who finds unwinding lists boring?+1
whitfin 2014

3
이 예를 이해하기가 정말 어렵습니다. :(
RBz

@RBz Stream 작업은 특히 하나 이상의 작업이 관련된 경우 이해하기가 쉽지 않습니다. 그래도 연습 문제입니다. 당신이 할 수있는 최선의 방법은 샘플에서 불분명 한 단어를 모두 구글 검색하여 직접 사용하는 것입니다. 사실 일반적인 명령형 샘플은 이해하기 훨씬 쉬웠을 것입니다 (때로는 더 빠름). 그러니 정말로 스트림을 사용해야하는지 생각해보십시오.
Aleksandr Kravets

답장 해주셔서 감사합니다. 그러나 나는 스트림 개념에 꽤 괜찮습니다. 여기서 문제가되는 것은 구체적인 예입니다. 나는 Time api를 잘 사용하지 못했지만 그것을 읽어도 여기서 무슨 일이 일어나고 있는지 이해하는 데 도움이되지 않습니다. 내가 순진 할 수도 있지만 답변에 대한 설명이 조금 더 있으면 좋을 것입니다. 당신의 모범을 이해하는 데 정말 도움이 될 것입니다. 알아, 난 그냥 호기심에 갇 혔어! :)
RBz

놀라운 예 ... 처음에는 이해하기 조금 어렵지만 일단 IDE에서 실행하면 ... 너무 강력한 대안 !! 고마워요!
Cristiano

2

이 메서드는 하나의 함수를 인수로 취하고,이 함수는 하나의 매개 변수 T를 입력 인수로 받아들이고, 매개 변수 R의 한 스트림을 반환 값으로 반환합니다. 이 함수가이 스트림의 각 요소에 적용되면 새 값의 스트림을 생성합니다. 그런 다음 각 요소에 의해 생성 된 이러한 새 스트림의 모든 요소는이 메서드의 반환 값이되는 새 스트림에 복사됩니다.

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


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

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 );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.