Bir akıştan ardışık çiftler toplayın


103

Gibi bir akış verildiğinde { 0, 1, 2, 3, 4 },

onu en zarif şekilde verilen forma nasıl dönüştürebilirim:

{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }

(tabii ki, çift sınıfını tanımladığımı varsayarak)?

Düzenleme: Bu kesinlikle girişler veya ilkel akışlarla ilgili değildir. Cevap, her türden bir akış için genel olmalıdır.


2
FP'deki terim "bölüm", ancak Java'da istenen anlambilimle bir yöntem bulamıyorum. Bir yüklem üzerinde bölümlemeye sahiptir.
Marko Topolnik

1
Tipik olarak, JDK 8'deki ayırıcı, geçiş ve bölümleme amaçları için düşünülür. Ben de bir örnek oluşturmaya çalışacağım.
Olimpiu POP

list.stream().map(i -> new Pair(i, i+1));
aepurniet

2
Eşdeğer akış dışı soru için bkz. Stackoverflow.com/questions/17453022/…
Raedwald

Bu arada, bazı insanlar her iki uygulamayı Map.Entryda bir Pair sınıfı olarak kullanır. (Bazıları hacklemeyi düşünebilir, ancak yerleşik bir sınıf kullanmak kullanışlı olabilir.)
Basil Bourque

Yanıtlar:


33

Standart akışları genişleten My StreamEx kitaplığı pairMap, tüm akış türleri için bir yöntem sağlar . İlkel akışlar için akış türünü değiştirmez, ancak bazı hesaplamalar yapmak için kullanılabilir. En yaygın kullanım, farklılıkları hesaplamaktır:

int[] pairwiseDiffs = IntStreamEx.of(input).pairMap((a, b) -> (b-a)).toArray();

Nesne akışı için herhangi bir başka nesne türü oluşturabilirsiniz. Kitaplığım, Pair(bu, kütüphane konseptinin bir parçasıdır) gibi kullanıcı tarafından görülebilen yeni veri yapıları sağlamıyor . Ancak kendi Pairsınıfınız varsa ve onu kullanmak istiyorsanız, aşağıdakileri yapabilirsiniz:

Stream<Pair> pairs = IntStreamEx.of(input).boxed().pairMap(Pair::new);

Veya zaten varsa Stream:

Stream<Pair> pairs = StreamEx.of(stream).pairMap(Pair::new);

Bu işlevsellik, özel ayırıcı kullanılarak uygulanır . Oldukça düşük bir ek yüke sahiptir ve güzel bir şekilde paralel olabilir. Tabii ki, diğer birçok çözüm gibi sadece rastgele erişim listesi / dizisi ile değil, herhangi bir akış kaynağıyla çalışır. Birçok testte gerçekten iyi performans gösteriyor. İşte farklı yaklaşımlar kullanılarak daha büyük bir değere önceki tüm giriş değerleri bulmak nerede bir JMH kriter (bkz bu soruyu).


Teşekkür ederim! Bu kütüphaneyi ne kadar çok çalışırsam, onu o kadar çok seviyorum. Sonunda akışları kullanmaya başlayabilirim. ( StreamExuygular Iterable! Yaşasın!)
Aleksandr Dubinsky

Cevabınızın% 100 tamamlanması için, a'yı nasıl Streamiçine alacağınızı gösterebilir misiniz StreamEx?
Aleksandr Dubinsky

3
@AleksandrDubinsky: sadece kullanın StreamEx.of(stream). Akışı Collection, diziden Readervb. Oluşturmak için başka uygun statik yöntemler de vardır . Cevabı düzenledi.
Tagir Valeev

@TagirValeev pairMapsıralı akışlarda mı sipariş ediliyor ? Aslında, forPairsOrdered () 'a sahip olmak isterdim, ancak böyle bir yöntem olmadığı için bir şekilde simüle edebilir miyim? stream.ordered().forPairs()veya stream().pairMap().forEachOrdered()?
Askar Kalykov

1
@AskarKalykov, pairMapmüdahale etmeyen durum bilgisiz eşleyici işlevi ile ara işlemdir, sıralama basitle aynı şekilde onun için belirtilmez map. forPairsTarifname ile sırasız, ancak düzensiz işlemleri ardışık akışları için sipariş de-facto. Daha fazla bağlam sağlamak için orijinal probleminizi ayrı bir yığın akışı sorusu olarak formüle etmeniz iyi olur.
Tagir Valeev

74

Java 8 akış kitaplığı esas olarak akışları paralel işleme için daha küçük parçalara bölmeye yöneliktir, bu nedenle durum bilgisi olan işlem hattı aşamaları oldukça sınırlıdır ve mevcut akış öğesinin dizinini almak ve bitişik akış öğelerine erişmek gibi şeyler yapmak desteklenmez.

Kuşkusuz, bazı sınırlamalarla birlikte bu sorunları çözmenin tipik bir yolu, akışı indekslerle çalıştırmak ve değerlerin, öğelerin geri alınabileceği bir ArrayList gibi bazı rasgele erişimli veri yapısında işlenmesine güvenmektir. Değerler içeride olsaydı, aşağıdaki arrayListgibi bir şey yaparak istendiği gibi çiftler üretilebilirdi:

    IntStream.range(1, arrayList.size())
             .mapToObj(i -> new Pair(arrayList.get(i-1), arrayList.get(i)))
             .forEach(System.out::println);

Elbette sınırlama, girdinin sonsuz bir akış olamayacağıdır. Yine de bu boru hattı paralel olarak çalıştırılabilir.


5
"Giriş sonsuz bir akış olamaz." Aslında, girdi bir akış olamaz. İnput ( arrayList) aslında bir koleksiyon, bu yüzden onu cevap olarak işaretlemedim. (Ama altın rozetiniz için tebrikler!)
Aleksandr Dubinsky

16

Bu zarif değil, hack'lenmiş bir çözüm, ancak sonsuz akışlar için çalışıyor

Stream<Pair> pairStream = Stream.iterate(0, (i) -> i + 1).map( // natural numbers
    new Function<Integer, Pair>() {
        Integer previous;

        @Override
        public Pair apply(Integer integer) {
            Pair pair = null;
            if (previous != null) pair = new Pair(previous, integer);
            previous = integer;
            return pair;
        }
    }).skip(1); // drop first null

Artık yayınınızı istediğiniz uzunlukta sınırlayabilirsiniz

pairStream.limit(1_000_000).forEach(i -> System.out.println(i));

Not: Umarım daha iyi bir çözüm vardır, tıkanma gibi bir şey(partition 2 1 stream)


6
Anonim sınıfların bazen lambdalara göre yararlı bir alternatif olduğunu belirtmek için tebrikler.
Aleksandr Dubinsky

2
@aepurniet Doğru çalışmayacağını varsayıyorum. parallelStream
Dokümana

14
Bu, akış çerçevesinin tasarımına tamamen aykırıdır ve anonim işlev durumsuz olmadığından doğrudan harita API sözleşmesini ihlal eder . Bunu paralel bir akışla ve daha fazla veriyle çalıştırmayı deneyin, böylece akış çerçevesi daha fazla çalışan iş parçacığı oluşturur ve sonucu göreceksiniz: Yeterli veriye sahip olana kadar (üretimde mi?) Yeniden üretilmesi neredeyse imkansız olan nadir rastlantısal "hatalar" ve tespit edilmesi zor. Bu felaket olabilir.
Mario Rossi

4
@AleksandrDubinsky Limit / skip'in paralelleştirilebilir olması konusunda yanılıyorsunuz; JDK'da sağlanan uygulama aslında paralel olarak çalışır. İşlem karşılaşma sırasına bağlı olduğundan, paralelleştirme her zaman bir performans avantajı sağlamayabilir, ancak yüksek Q durumlarında sağlayabilir.
Brian Goetz

4
@AleksandrDubinsky Yanlış. Akış sırasızsa (tanımlanmış karşılaşma sırası yoksa , mantıksal olarak "birinci" veya "n'inci" öğe yoktur, yalnızca öğeler vardır.) Ancak akış ister sıralı ister sırasız olsun, atlama her zaman mümkün olmuştur. paralel çalışmak. Akış sıralıysa çıkarılacak daha az paralellik vardır, ancak yine de paraleldir.
Brian Goetz

15

Orijinal ayırıcıdan her nöğeyi Talan ve üreten bir ayırıcı sarıcı uyguladım List<T>:

public class ConsecutiveSpliterator<T> implements Spliterator<List<T>> {

    private final Spliterator<T> wrappedSpliterator;

    private final int n;

    private final Deque<T> deque;

    private final Consumer<T> dequeConsumer;

    public ConsecutiveSpliterator(Spliterator<T> wrappedSpliterator, int n) {
        this.wrappedSpliterator = wrappedSpliterator;
        this.n = n;
        this.deque = new ArrayDeque<>();
        this.dequeConsumer = deque::addLast;
    }

    @Override
    public boolean tryAdvance(Consumer<? super List<T>> action) {
        deque.pollFirst();
        fillDeque();
        if (deque.size() == n) {
            List<T> list = new ArrayList<>(deque);
            action.accept(list);
            return true;
        } else {
            return false;
        }
    }

    private void fillDeque() {
        while (deque.size() < n && wrappedSpliterator.tryAdvance(dequeConsumer))
            ;
    }

    @Override
    public Spliterator<List<T>> trySplit() {
        return null;
    }

    @Override
    public long estimateSize() {
        return wrappedSpliterator.estimateSize();
    }

    @Override
    public int characteristics() {
        return wrappedSpliterator.characteristics();
    }
}

Ardışık bir akış oluşturmak için aşağıdaki yöntem kullanılabilir:

public <E> Stream<List<E>> consecutiveStream(Stream<E> stream, int n) {
    Spliterator<E> spliterator = stream.spliterator();
    Spliterator<List<E>> wrapper = new ConsecutiveSpliterator<>(spliterator, n);
    return StreamSupport.stream(wrapper, false);
}

Örnek kullanım:

consecutiveStream(Stream.of(0, 1, 2, 3, 4, 5), 2)
    .map(list -> new Pair(list.get(0), list.get(1)))
    .forEach(System.out::println);

Bu her öğeyi iki kez tekrarlıyor mu?
Aleksandr Dubinsky

Hayır! List<E>Öğeler içeren yeni bir akış oluşturur . Her liste n, orijinal akıştan ardışık öğeler içerir . Kendiniz kontrol edin;)
Tomek Rękawek

Cevabınızı her öğenin (birinci ve sonuncu hariç) tekrarlanacağı şekilde değiştirebilir misiniz?
Aleksandr Dubinsky

4
+1 Bence bu iyi bir iş ve bölüm boyutuna ek olarak herhangi bir adım boyutuna genelleştirilmesi gerekiyor. Bir (partition size step)işleve çok fazla ihtiyaç vardır ve bu, onu elde etmenin en iyi yoludur.
Marko Topolnik

3
ArrayDequeTercihen performans için kullanmayı düşünün LinkedList.
Marko Topolnik

14

Bunu Stream.reduce () yöntemiyle yapabilirsiniz (Bu tekniği kullanan başka bir yanıt görmedim).

public static <T> List<Pair<T, T>> consecutive(List<T> list) {
    List<Pair<T, T>> pairs = new LinkedList<>();
    list.stream().reduce((a, b) -> {
        pairs.add(new Pair<>(a, b));
        return b;
    });
    return pairs;
}

1
(1,2) (3,4) yerine (1,2) (2,3) döndürür. Ayrıca sırayla uygulanıp uygulanmayacağından emin değilim (kesinlikle bunun garantisi yok).
Aleksandr Dubinsky

1
Lütfen istenen davranış olan soruyu kontrol edin @Aleksandr Dubinsky
SamTebbs33

4
Ahh, evet, üzgünüm. Ve düşünmek için yazdım.
Aleksandr Dubinsky


6

Bunu cyclops-react'te (bu kitaplığa katkıda bulunuyorum) kaydırma operatörünü kullanarak yapabilirsiniz.

  LazyFutureStream.of( 0, 1, 2, 3, 4 )
                  .sliding(2)
                  .map(Pair::new);

Veya

   ReactiveSeq.of( 0, 1, 2, 3, 4 )
                  .sliding(2)
                  .map(Pair::new);

Pair yapıcısının 2 öğeli bir Koleksiyonu kabul edebileceğini varsayarsak.

4'e göre gruplamak ve 2'ye kadar artırmak istiyorsanız, bu da desteklenir.

     ReactiveSeq.rangeLong( 0L,Long.MAX_VALUE)
                .sliding(4,2)
                .forEach(System.out::println);

Java.util.stream.Stream üzerinde kayan bir görünüm oluşturmak için eşdeğer statik yöntemler, cyclops -streams StreamUtils sınıfında da sağlanır .

       StreamUtils.sliding(Stream.of(1,2,3,4),2)
                  .map(Pair::new);

Not: - tek iş parçacıklı işlem için ReactiveSeq daha uygun olacaktır. LazyFutureStream, ReactiveSeq'i genişletir, ancak öncelikle eşzamanlı / paralel kullanım için tasarlanmıştır (bu bir Vadeli İşlem Akışıdır).

LazyFutureStream, Seq'i harika jOOλ'dan (java.util.stream.Stream'i genişleten) genişleten ReactiveSeq'i genişletir, böylece Lukas'ın sunduğu çözümler her iki Stream türü ile de çalışır. İlgilenen herkes için pencere / kayan operatörler arasındaki temel farklar, açık göreceli güç / karmaşıklık değiş tokuşu ve sonsuz akışlarla kullanıma uygunluktur (kayma akışı tüketmez, ancak akarken tamponlar).


Bu şekilde [(0,1) (2,3) ...] elde edersiniz ama soru [(0,1) (1,2) ...] diye sorar. Lütfen RxJava ile ilgili cevabımı görün ...
frhack

1
Haklısın, benim hatam, soruyu yanlış anladım - burada kayar operatör kullanmak doğru olanıdır. Cevabımı güncelleyeceğim - teşekkürler!
John McClean

4

Proton-pack kütüphane pencereli functionnality sağlar. Bir Pair sınıfı ve bir Akış verildiğinde, bunu şu şekilde yapabilirsiniz:

Stream<Integer> st = Stream.iterate(0 , x -> x + 1);
Stream<Pair<Integer, Integer>> pairs = StreamUtils.windowed(st, 2, 1)
                                                  .map(l -> new Pair<>(l.get(0), l.get(1)))
                                                  .moreStreamOps(...);

Artık pairsakış şunları içerir:

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, ...) and so on

Ancak, stiki kez oluşturmanız gerekiyor gibi görünüyor ! Bu kütüphane, sorunu tek bir akış kullanarak çözebilir mi?
Aleksandr Dubinsky

@AleksandrDubinsky Sanmıyorum, bu yüzden mevcut ayırıcılarla kullanılabilir. Bir sorun gönderdim github.com/poetix/protonpack/issues/9
Alexis C.

@AleksandrDubinsky windowedİşlevsellik eklendi! Düzenlemeye bakın.
Alexis C.

1
Diğer kullanıcıların geçmişi değil çözümü görebilmesi için neden eski cevabınızı silmiyorsunuz?
Aleksandr Dubinsky

4

Ardışık çiftleri bulmak

Üçüncü taraf bir kitaplık kullanmak istiyorsanız ve paralelliğe ihtiyacınız yoksa, jOOλ aşağıdaki gibi SQL tarzı pencere işlevleri sunar

System.out.println(
Seq.of(0, 1, 2, 3, 4)
   .window()
   .filter(w -> w.lead().isPresent())
   .map(w -> tuple(w.value(), w.lead().get())) // alternatively, use your new Pair() class
   .toList()
);

Verimli

[(0, 1), (1, 2), (2, 3), (3, 4)]

lead()Fonksiyon penceresinden kastetmek için sonraki değeri erişir.

Ardışık üçlü / dörtlü / n-tupleleri bulma

Yorumlardaki bir soru, çiftlerin değil n-tuplelerin (veya muhtemelen listelerin) toplanması gereken daha genel bir çözüm istiyordu. İşte alternatif bir yaklaşım:

int n = 3;

System.out.println(
Seq.of(0, 1, 2, 3, 4)
   .window(0, n - 1)
   .filter(w -> w.count() == n)
   .map(w -> w.window().toList())
   .toList()
);

Bir liste listesi oluşturma

[[0, 1, 2], [1, 2, 3], [2, 3, 4]]

Olmadan filter(w -> w.count() == n)sonuç şöyle olur

[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4], [4]]

Sorumluluk reddi: jOOλ'nın arkasındaki şirket için çalışıyorum


İlginç. Ya 3 veya daha fazla öğeyi gruplamam gerekirse? Kullan w.lead().lead()?
Raul Santelices

1
@RaulSantelices: tuple(w.value(), w.lead(1), w.lead(2))bir seçenek olabilir. Cevabımı daha genel bir çözümle güncelledimlength = n
Lukas Eder

1
.window()Tüm girdi akışını bir ara koleksiyona toplayan ve ardından ondan yeni bir akış oluşturan tembel işlem olmadığını doğru bir şekilde anlıyorum.
Tagir Valeev

@TagirValeev: Evet, mevcut uygulama bu. Yukarıdaki durumda (hayır Comparatoro gibi bir optimizasyon, sipariş pencereler için kullanılır) bu mümkün olurdu ve muhtemelen gelecekte uygulanacak olmaktır.
Lukas Eder


2

Biz kullanabilirsiniz RxJava (çok güçlü reaktif uzatma kütüphanesi)

IntStream intStream  = IntStream.iterate(1, n -> n + 1);

Observable<List<Integer>> pairObservable = Observable.from(intStream::iterator).buffer(2,1);

pairObservable.take(10).forEach(b -> {
            b.forEach(n -> System.out.println(n));
            System.out.println();
        });

Tampon operatörü bir gözlemlenebilir içine yayar ürün yayar bu öğelerin koleksiyonları tamponlu o bir gözlemlenebilir dönüştüren ..


1
Observable.zip(obs, obs.skip(1), pair->{...})Şimdiye kadar kullandım ! Observable.bufferAdım içeren bir sürümün olduğunu bilmiyordum (ve ben zippython'daki numaraya alışkınım ). +1
Reut Sharabani

1

Operasyon yani gerçekten akışları çözmek için ne anlama geldiğini aslında durumsal olması - in "Vatansız Davranışlar" bölümüne bakın javadoc :

En iyi yaklaşım, işlemlerin tamamen akışını sağlamak için durum bilgisi olan davranış parametrelerinden kaçınmaktır.

Buradaki bir çözüm, akışınıza durumu harici bir sayaç aracılığıyla tanıtmaktır, ancak bu yalnızca sıralı bir akışla çalışacaktır.

public static void main(String[] args) {
    Stream<String> strings = Stream.of("a", "b", "c", "c");
    AtomicReference<String> previous = new AtomicReference<>();
    List<Pair> collect = strings.map(n -> {
                            String p = previous.getAndSet(n);
                            return p == null ? null : new Pair(p, n);
                        })
                        .filter(p -> p != null)
                        .collect(toList());
    System.out.println(collect);
}


static class Pair<T> {
    private T left, right;
    Pair(T left, T right) { this.left = left; this.right = right; }
    @Override public String toString() { return "{" + left + "," + right + '}'; }
}

Soru, yalnızca ardışık tam sayıları toplamayı değil, bir girdi akışının ardışık öğelerini toplamayı ister. Terminolojinin önemli bir açıklaması Stream:! = "Lambdas".
Aleksandr Dubinsky

AtomicInteger'ı bir AtomicReference ile değiştirebilirsiniz. Alternatif, kendi toplayıcınızı yuvarlamak veya bu örnekteki gibi harici kitaplıkları kullanmaktır: stackoverflow.com/a/30090528/829571
assylias

Düzenlememe bakın. Ayrıca lambda! = Stream hakkındaki yorumunuzu anladığımdan emin değilim. Anonim bir sınıf kullanan diğer yanıt, esasen aynı şeyi yapar, ancak devlet harici olmak yerine anonim sınıf tarafından tutulur ...
assylias

1
Bu işe yarıyor. StreamExKütüphanede ayrıca iyi bulmak ve başlı başına bir cevap olabilir. "Akışlar! = Lambdas" hakkındaki yorumum, "İşlem aslında durum bilgili olduğundan, lambdaların gerçekten çözmesi gereken şey değil." Sanırım "akışlar" kelimesini kullanmak istedin.
Aleksandr Dubinsky

Oh anlıyorum - bunu açıklığa kavuşturdum.
assylias

0

Sizin durumunuzda, geçirilen son int'in kaydını tutan ve bunu orijinal IntStream'i eşlemek için kullanan özel IntFunction'ımı yazardım.

import java.util.function.IntFunction;
import java.util.stream.IntStream;

public class PairFunction implements IntFunction<PairFunction.Pair> {

  public static class Pair {

    private final int first;
    private final int second;

    public Pair(int first, int second) {
      this.first = first;
      this.second = second;
    }

    @Override
    public String toString() {
      return "[" + first + "|" + second + "]";
    }
  }

  private int last;
  private boolean first = true;

  @Override
  public Pair apply(int value) {
    Pair pair = !first ? new Pair(last, value) : null;
    last = value;
    first = false;
    return pair;
  }

  public static void main(String[] args) {

    IntStream intStream = IntStream.of(0, 1, 2, 3, 4);
    final PairFunction pairFunction = new PairFunction();
    intStream.mapToObj(pairFunction)
        .filter(p -> p != null) // filter out the null
        .forEach(System.out::println); // display each Pair

  }

}

Bununla ilgili sorun, vatansızlığı pencereden dışarı atmasıdır.
Rob

@Rob ve bununla ilgili sorun nedir?
Aleksandr Dubinsky

Lambda'nın ana noktalarından biri, dahili entegratörlerin işi paralelleştirebilmesi için değişken duruma sahip olmamasıdır.
Rob

@Rob: Evet, haklısınız, ancak verilen örnek akış paralelliği yine de meydan okuyor çünkü her öğe (birinci ve sonuncusu hariç) bir çiftin birinci ve ikinci öğesi olarak kullanılıyor.
jpvee

@jpvee evet, düşündüğün şeyin bu olduğunu anladım. Bunu başka bir haritacı ile yapmanın bir yolu olup olmadığını merak ediyorum. Temelde ihtiyacınız olan tek şey, döngü artırıcıyı ikişer ikişer yapmak ve ardından functorun 2 argüman almasını sağlamaktır. Bu mümkün olmalı.
Rob

0

Bir zaman-serisi zaman (x değerleri) birbirini takip eden farkların hesaplanması için, kullanımı stream'in collect(...)yöntemi:

final List< Long > intervals = timeSeries.data().stream()
                    .map( TimeSeries.Datum::x )
                    .collect( DifferenceCollector::new, DifferenceCollector::accept, DifferenceCollector::combine )
                    .intervals();

DifferenceCollector'ın böyle bir şey olduğu yerde:

public class DifferenceCollector implements LongConsumer
{
    private final List< Long > intervals = new ArrayList<>();
    private Long lastTime;

    @Override
    public void accept( final long time )
    {
        if( Objects.isNull( lastTime ) )
        {
            lastTime = time;
        }
        else
        {
            intervals.add( time - lastTime );
            lastTime = time;
        }
    }

    public void combine( final DifferenceCollector other )
    {
        intervals.addAll( other.intervals );
        lastTime = other.lastTime;
    }

    public List< Long > intervals()
    {
        return intervals;
    }
}

Muhtemelen bunu ihtiyaçlarınıza uyacak şekilde değiştirebilirsiniz.


0

Sonunda, Değer çiftleriyle düzgün bir şekilde başa çıkabilmek için Stream.reduce'u kandırmanın bir yolunu buldum; JDK 8'de doğal olarak görünmeyen bu özelliği gerektiren çok sayıda kullanım durumu vardır :

public static int ArithGeo(int[] arr) {
    //Geometric
    List<Integer> diffList = new ArrayList<>();
    List<Integer> divList = new ArrayList<>();
    Arrays.stream(arr).reduce((left, right) -> {
        diffList.add(right-left);
        divList.add(right/left);
        return right;
    });
    //Arithmetic
    if(diffList.stream().distinct().count() == 1) {
        return 1;
    }
    //Geometric
    if(divList.stream().distinct().count() == 1) {
        return 2;
    }
    return -1;
}

Kullandığım numara geri dönüş hakkı; Beyan.


1
Bunun reduceişe yaraması için yeterli garanti verdiğini sanmıyorum .
Aleksandr Dubinsky

Yeterli garantiler hakkında daha fazla bilgi edinmek isterim . Lütfen detaylandırır mısınız? Belki Guava'da bir alternatif vardır ... ama kısıtlıyım ve kullanamıyorum.
Beezer

-1

Zip kullanmak zarif bir çözüm olacaktır . Gibi bir şey:

List<Integer> input = Arrays.asList(0, 1, 2, 3, 4);
Stream<Pair> pairStream = Streams.zip(input.stream(),
                                      input.stream().substream(1),
                                      (a, b) -> new Pair(a, b)
);

Bu oldukça özlü ve zariftir, ancak girdi olarak bir liste kullanır. Sonsuz bir akış kaynağı bu şekilde işlenemez.

Diğer bir (çok daha zahmetli) sorun, tüm Streams sınıfıyla birlikte zip'in son zamanlarda API'den kaldırılmış olmasıdır. Yukarıdaki kod yalnızca b95 veya daha eski sürümlerde çalışır. Bu yüzden, en son JDK ile şık bir FP tarzı çözüm olmadığını söyleyebilirim ve şu anda sadece bir şekilde zip'in API'ye yeniden ekleneceğini umabiliriz.


Nitekim zipkaldırıldı. Ben ne olduğunu tüm hatırlamıyorum Streamssınıfta, ama bazı şeyler üzerinde statik yöntemler olduğu göç etmiş Streambir arayüz ve de vardır StreamSupportve Stream.Buildersınıflar.
Stuart Marks

Doğru. Concat veya iterate gibi diğer bazı yöntemler taşınmış ve Stream'de varsayılan yöntemler haline gelmiştir. Ne yazık ki zip, API'den kaldırıldı. Bu seçimin arkasındaki nedenleri anlıyorum (örn. Tuple eksikliği) ama yine de güzel bir özellikti.
gadget

2
@gadget Tuple'ların ne ile ilgisi var zip? Bilgiçlikçi bir neden icat edilmiş olursa olsun, öldürmeyi haklı çıkarmaz zip.
Aleksandr Dubinsky

@AleksandrDubinsky Çoğu durumda zip, çıktı olarak bir Çiftler / Tuple koleksiyonu oluşturmak için kullanılır. Onlar savundu onlar saklasak zip insanlar da JDK bir parçası olarak Tuples talep edeceğini söyledi. Yine de var olan bir özelliği asla kaldırmazdım.
gadget

-1

Bu ilginç bir sorundur. Benim mi hibrid hiçbir üründe aşağıda girişimi?

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3);
    Iterator<Integer> first = list.iterator();
    first.next();
    if (first.hasNext())
        list.stream()
        .skip(1)
        .map(v -> new Pair(first.next(), v))
        .forEach(System.out::println);
}

Paralel işlemeye uygun olmadığına inanıyorum ve bu nedenle diskalifiye edilebilir.


Sorusu paralel işlem için sormadım ama biz sadece bir olduğunu varsayalım ki Stream, bir değil List. Tabii ki, bir yineleyiciyi bir Akıştan da alabiliriz, bu nedenle bu geçerli bir çözüm olabilir. Yine de, orijinal bir yaklaşım.
Aleksandr Dubinsky

-1

Başkalarının da gözlemlediği gibi, sorunun doğası gereği, bazı durumsallık gereklidir.

Esasen Oracle SQL işlevi LEAD olanı istediğim benzer bir sorunla karşı karşıya kaldım. Bunu uygulama girişimim aşağıdadır.

/**
 * Stream that pairs each element in the stream with the next subsequent element.
 * The final pair will have only the first item, the second will be null.
 */
<T> Spliterator<Pair<T>> lead(final Stream<T> stream)
{
    final Iterator<T> input = stream.sequential().iterator();

    final Iterable<Pair<T>> iterable = () ->
    {
        return new Iterator<Pair<T>>()
        {
            Optional<T> current = getOptionalNext(input);

            @Override
            public boolean hasNext()
            {
                return current.isPresent();
            }

            @Override
            public Pair<T> next()
            {
                Optional<T> next = getOptionalNext(input);
                final Pair<T> pair = next.isPresent()
                    ? new Pair(current.get(), next.get())
                    : new Pair(current.get(), null);
                current = next;

                return pair;
            }
        };
    };

    return iterable.spliterator();
}

private <T> Optional<T> getOptionalNext(final Iterator<T> iterator)
{
    return iterator.hasNext()
        ? Optional.of(iterator.next())
        : Optional.empty();
}

-1

Akış boyunca akan öğeleri depolamak için sınırlı bir kuyruk kullanarak bunu başarabilirsiniz (bu, burada ayrıntılı olarak açıkladığım fikre dayanmaktadır: Akışta bir sonraki öğeyi elde etmek mümkün müdür? )

Aşağıdaki örnek ilk olarak akıştan geçen öğeleri depolayacak BoundedQueue sınıfının örneğini tanımlar (LinkedList'i genişletme fikrinden hoşlanmıyorsanız, alternatif ve daha genel yaklaşım için yukarıda belirtilen bağlantıya bakın). Daha sonra, sonraki iki öğeyi Eş örneğinde birleştirirsiniz:

public class TwoSubsequentElems {
  public static void main(String[] args) {
    List<Integer> input = new ArrayList<Integer>(asList(0, 1, 2, 3, 4));

    class BoundedQueue<T> extends LinkedList<T> {
      public BoundedQueue<T> save(T curElem) {
        if (size() == 2) { // we need to know only two subsequent elements
          pollLast(); // remove last to keep only requested number of elements
        }

        offerFirst(curElem);

        return this;
      }

      public T getPrevious() {
        return (size() < 2) ? null : getLast();
      }

      public T getCurrent() {
        return (size() == 0) ? null : getFirst();
      }
    }

    BoundedQueue<Integer> streamHistory = new BoundedQueue<Integer>();

    final List<Pair<Integer>> answer = input.stream()
      .map(i -> streamHistory.save(i))
      .filter(e -> e.getPrevious() != null)
      .map(e -> new Pair<Integer>(e.getPrevious(), e.getCurrent()))
      .collect(Collectors.toList());

    answer.forEach(System.out::println);
  }
}

-3

@Aepurniet'e katılıyorum ancak bunun yerine mapToObj kullanmanız gerekiyor

range(0, 100).mapToObj((i) -> new Pair(i, i+1)).forEach(System.out::println);

1
Sağ. Ancak bu, bir akışın (herhangi bir türden) öğe çiftlerini değil, tam sayı çiftlerini toplar.
Aleksandr Dubinsky

-5

for0'dan length-1akışınızın sonuna kadar uzanan bir döngü çalıştırın

for(int i = 0 ; i < stream.length-1 ; i++)
{
    Pair pair = new Pair(stream[i], stream[i+1]);
    // then add your pair to an array
}

3
ve çözümün lambda kısmı nerede?
Olimpiu POP

Akışın sonsuz olduğu durum böyle değil
mishadoff

@Olimpiu - Lambda'yı nereden aldın bir gereklilikti? Kaçırmadığımdan emin olmak için soruyu iki kez okudum. Ayrıca düzenleme geçmişini de kontrol ettim. Ve soru onunla etiketlenmemiş.
jww
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.