Java 8 Streams FlatMap yöntemi örneği


86

Ben yaklaşan kontrol edilmiştir Java updateyani: Java 8 or JDK 8. Evet, sabırsızım, birçok yeni şey var ama anlamadığım bir şey var, basit bir kod:

final Stream<Integer>stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream.flatMap();

javadoc'lar

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

Bu akışın her bir öğesinin, sağlanan eşleme işlevinin her öğeye uygulanmasıyla üretilen eşlenmiş bir akışın içeriğiyle değiştirilmesinin sonuçlarından oluşan bir akış döndürür. Eşlenen her akış, içeriği bu akışa yerleştirildikten sonra kapatılır. (Eşlenmiş bir akış boşsa, bunun yerine boş bir akış kullanılır.) Bu bir ara işlemdir.

Biri flatMap, önceki java sürümlerinde bunu Java[6,7]nasıl kodlayabileceğiniz ve aynı rutinleri kullanarak nasıl kodlayabileceğiniz hakkında bazı basit gerçek hayat örnekleri oluştursa çok memnun olurum Java 8.


2
İnternette flatMap (en azından Scala için ve temelde aynılar :)) kullanmanın yaklaşık bir milyon örneği var, aramayı denedin mi? Başlamak için bir tane var: brunton-spall.co.uk/post/2011/12/02/…
Peter Svensson


Demek istediğim, flatMap şu anda Java ve Scala'da var olan genel bir kavram.
Peter Svensson

ok bunun hakkında daha fazla okuyacağım teşekkürler dostum.
chiperortiz

10
Java'daki flatMap aynı fikirdir ancak akışlarda oldukça farklı görünür. İnsanları Scala'ya yönlendirmeyin!
orbfish

Yanıtlar:


158

Sorunuzda gösterdiğiniz gibi zaten düz olan flatMapbir Akış için bir anlam ifade etmiyor Stream<Integer>.

Ancak, eğer varsa, Stream<List<Integer>>o zaman mantıklı olur ve bunu yapabilirsin:

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

Hangisi yazdırır:

1
2
3
4
5

Bunu Java 8 öncesi yapmak için bir döngüye ihtiyacınız var:

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

Uydurma örnek

Şu sırayı oluşturmak istediğinizi düşünün: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 vb. (Diğer bir deyişle: 1x1, 2x2, 3x3 vb.)

Onunla flatMapşöyle görünebilir:

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

nerede:

  • IntStream.rangeClosed(1, 4)int1 ile 4 arasında bir akış oluşturur
  • IntStream.iterate(i, identity()).limit(i)i uzunluğunda bir akış oluşturur int- bu nedenle i = 4ona uygulandığında bir akış oluşturur:4, 4, 4, 4
  • flatMap Akışı "düzleştirir" ve orijinal akışa "birleştirir"

Java <8 ile iki iç içe döngüye ihtiyacınız olacaktır:

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

Gerçek dünya örneği

Diyelim ki List<TimeSeries>, her TimeSeriesbirinin esasen a olduğu bir Map<LocalDate, Double>. Zaman serilerinden en az birinin bir değere sahip olduğu tüm tarihlerin bir listesini almak istiyorum. flatMapkurtarmak için:

list.stream().parallel()
    .flatMap(ts -> ts.dates().stream()) // for each TS, stream dates and flatmap
    .distinct()                         // remove duplicates
    .sorted()                           // sort ascending
    .collect(toList());

Yalnızca okunabilir olmakla kalmaz, aynı zamanda aniden 100k öğeyi işlemeniz gerekirse, yalnızca eklemek parallel(), herhangi bir eşzamanlı kod yazmadan performansı artıracaktır.


14
Her iki örnek de kabul edilen cevaptan çok daha iyidir.
Sebastian Graf

derleyici kimlik () hakkında tanımsız
Nirmal

2
@ user3320018 statik içe aktarmanız gerekir Function.identity.
assylias

@assylias java.util.function.Function içe aktarmayı denedim ama işe yaramadı, java 8'de yeniyim ve bu java 8'e özgü olabilir veya olmayabilir, ancak lütfen bu hatayı nasıl kaldıracağımı söyler misiniz?
Nirmal

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

18

Bir kelime öbeği listesinden ASC ile sıralanmış benzersiz kelimeleri çıkarın:

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

... ve çıktı:

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

11

Çözülmeyen listeleri sıkıcı bulan tek kişi ben miyim? ;-)

Nesnelerle deneyelim. Bu arada gerçek dünya örneği.

Verilen: Tekrarlayan görevi temsil eden nesne. Önemli görev alanları hakkında: Her zaman hatırlatıcılar çalmaya başlar startve tekrar eder repeatPeriod repeatUnit(örneğin 5 SAAT) ve repeatCounttoplamda hatırlatıcılar (başlangıç ​​dahil) olacaktır.

Hedef: her görev hatırlatma çağrısı için bir görev kopyası listesi elde edin.

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

Çıktı:

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}

Not: Birisi daha basit bir çözüm önerirse memnun olurum, sonuçta ben profesyonel değilim.

GÜNCELLEME: @RBz ayrıntılı bir açıklama istedi, işte burada. Temel olarak flatMap, başka bir akışın içindeki akışlardaki tüm öğeleri çıkış akışına yerleştirir. Burada birçok akış var :). Bu nedenle, ilk akıştaki her Görev için lambda ifadesi x -> LongStream.iterate..., görev başlangıç ​​anlarını temsil eden uzun değerler akışı oluşturur. Bu akış x.getRepeatCount()örneklerle sınırlıdır . Değerleri başlar x.getStart().toEpochSecond(ZoneOffset.UTC)ve sonraki her değer lambda kullanılarak hesaplanır p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds(). boxed()Uzun sarmalayıcı örneği olarak her bir uzun değere sahip akışı döndürür. Ardından bu akıştaki her Long, artık tekrar etmeyen ve tam yürütme süresini içeren yeni Görev örneğiyle eşlenir. Bu örnek, giriş listesinde yalnızca bir Görev içeriyor. Ama bin tane olduğunu hayal et. Daha sonra 1000 Task nesnesi akışına sahip olacaksınız. Ve neflatMapburada, tüm akışlardaki tüm Görevleri aynı çıktı akışına koyuyor. Anladığım kadarıyla hepsi bu. Sorunuz için teşekkürler!


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

3
Bu örneği anlamakta gerçekten zorlanıyorum. :(
RBz

@RBz Stream işlemleri, özellikle birden fazla işlem söz konusu olduğunda anlaşılması bazen kolay değildir. Yine de bu bir pratik meselesi. Yapabileceğiniz en iyi şey, örnekteki net olmayan her kelimeyi google'da arayıp kendiniz kullanmaya çalışmaktır. Aslında, her zamanki emir kipi stil örneğini anlamak çok daha kolay (ve bazen daha hızlı) olurdu. Bu yüzden, akışları gerçekten kullanmanız gerekip gerekmediğini bir düşünün.
Aleksandr Kravets

Cevap için teşekkürler dostum. Ancak, akış kavramları konusunda oldukça iyiyim. Burada sorun yaşadığım şey, örneğe özeldir. Time api ile o kadar iyi değildim, ancak bir okuma bile burada neler olduğunu anlamama yardımcı olmuyor. Belki saf davranıyorum, ama cevabınız için biraz daha açıklama yapmak harika olacak. Örneğinizi anlamama gerçekten yardımcı olur. Biliyorum, sadece meraktan kilitlendim! :)
RBz

Şaşırtıcı bir örnek ... başlangıçta anlaşılması biraz zor, ama onu IDE'mde çalıştırdığımda ... çok güçlü bir alternatif !! çok teşekkürler !
Cristiano

2

Bu yöntem bağımsız değişken olarak bir İşlev alır, bu işlev bir T parametresini giriş bağımsız değişkeni olarak kabul eder ve bir R parametresi akışını bir dönüş değeri olarak döndürür. Bu işlev, bu akışın her bir öğesine uygulandığında, bir yeni değerler akışı üretir. Her öğe tarafından üretilen bu yeni akışların tüm öğeleri daha sonra bu yöntemin bir dönüş değeri olacak yeni bir akışa kopyalanır.

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


2

Çok basit bir örnek: İlk veya sondan bağımsız olarak adların bir listesini almak için tam adların bir listesini bölün

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

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

Bu çıktı:

Barry
Allen
Bruce
Wayne
Clark
Kent

1

Bu göz önüne alındığında:

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

    }

ve bu:

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

}

Daha sonra bunu yapabiliriz:

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 );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.