Kumpulkan pasangan yang berurutan dari aliran


102

Diberikan aliran seperti { 0, 1, 2, 3, 4 },

bagaimana cara paling elegan mengubahnya menjadi bentuk tertentu:

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

(dengan asumsi, tentu saja, saya telah mendefinisikan kelas Pair)?

Sunting: Ini bukan hanya tentang int atau aliran primitif. Jawabannya harus umum untuk aliran jenis apa pun.


2
Istilah dari FP adalah "partisi", tetapi saya tidak menemukan metode dengan semantik yang diinginkan di Java. Ini memiliki partisi berdasarkan predikat.
Marko Topolnik

1
Biasanya spliterator di JDK 8 dianggap untuk tujuan traverse dan partisi. Saya akan mencoba memberikan contoh juga.
Olimpiu POP

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

2
Untuk pertanyaan non-aliran yang setara, lihat stackoverflow.com/questions/17453022/…
Raedwald

Ngomong-ngomong, beberapa orang menggunakan salah satu implementasi Map.Entrysebagai kelas Pair. (Memang, beberapa mungkin menganggap itu peretasan, tetapi menggunakan kelas bawaan berguna.)
Basil Bourque

Jawaban:


33

Perpustakaan StreamEx saya yang memperluas aliran standar menyediakan pairMapmetode untuk semua jenis aliran. Untuk aliran primitif, ini tidak mengubah jenis aliran, tetapi dapat digunakan untuk membuat beberapa perhitungan. Penggunaan paling umum adalah untuk menghitung perbedaan:

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

Untuk aliran objek, Anda dapat membuat jenis objek lainnya. Perpustakaan saya tidak menyediakan struktur data baru yang terlihat oleh pengguna seperti Pair(itu adalah bagian dari konsep perpustakaan). Namun jika Anda memiliki Pairkelas Anda sendiri dan ingin menggunakannya, Anda dapat melakukan hal berikut:

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

Atau jika Anda sudah memiliki beberapa Stream:

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

Fungsionalitas ini diimplementasikan menggunakan pemisah khusus . Ini memiliki overhead yang cukup rendah dan dapat diparalelkan dengan baik. Tentu saja ini berfungsi dengan sumber aliran apa pun, bukan hanya daftar / larik akses acak seperti banyak solusi lainnya. Dalam banyak pengujian, alat ini bekerja dengan sangat baik. Berikut adalah patokan JMH di mana kita menemukan semua nilai input sebelum nilai yang lebih besar menggunakan pendekatan yang berbeda (lihat pertanyaan ini ).


Terima kasih! Semakin saya mempelajari perpustakaan ini, semakin saya menyukainya. Saya mungkin akhirnya mulai menggunakan aliran. ( StreamExmengimplementasikan Iterable! Hore!)
Aleksandr Dubinsky

Untuk membuat jawaban Anda 100% lengkap, dapatkah Anda menunjukkan cara membungkus Streama StreamEx?
Aleksandr Dubinsky

3
@ AleksandrDubinsky: gunakan saja StreamEx.of(stream). Ada metode statis lain yang mudah digunakan untuk membuat aliran dari Collection, larik Reader,, dll. Diedit jawabannya.
Tagir Valeev

@TagirValeev pairMapdipesan pada aliran berurutan? Sebenarnya, saya ingin memiliki forPairsOrdered (), tetapi karena tidak ada metode seperti itu, dapatkah saya mensimulasikannya? stream.ordered().forPairs()atau stream().pairMap().forEachOrdered()?
Askar Kalykov

1
@AskarKalykov, pairMapadalah operasi perantara dengan fungsi mapper stateless non-interferensi, pengurutan tidak ditentukan untuknya dengan cara yang sama seperti untuk sederhana map. Tidak forPairsdiurutkan menurut spesifikasi, tetapi operasi yang tidak diurutkan secara de-facto diurutkan untuk streaming berurutan. Akan lebih baik jika Anda merumuskan masalah asli Anda sebagai pertanyaan stackoverflow terpisah untuk memberikan lebih banyak konteks.
Tagir Valeev

74

Pustaka aliran Java 8 terutama ditujukan untuk memisahkan aliran menjadi potongan yang lebih kecil untuk pemrosesan paralel, sehingga tahapan pipeline stateful sangat terbatas, dan melakukan hal-hal seperti mendapatkan indeks elemen aliran saat ini dan mengakses elemen aliran yang berdekatan tidak didukung.

Cara khas untuk memecahkan masalah ini, dengan beberapa keterbatasan, tentu saja, adalah dengan mengarahkan aliran dengan indeks dan mengandalkan nilai yang diproses dalam beberapa struktur data akses-acak seperti ArrayList tempat elemen dapat diambil. Jika nilainya masuk arrayList, seseorang dapat menghasilkan pasangan seperti yang diminta dengan melakukan sesuatu seperti ini:

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

Tentu saja batasannya adalah input tidak bisa menjadi aliran yang tak terbatas. Pipeline ini bisa dijalankan secara paralel.


5
"Masukan tidak bisa menjadi aliran tak terbatas." Sebenarnya, masukan sama sekali tidak bisa berupa aliran. Input ( arrayList) sebenarnya adalah sebuah koleksi, itulah mengapa saya tidak menandainya sebagai jawaban. (Tapi selamat atas lencana emas Anda!)
Aleksandr Dubinsky

16

Ini tidak elegan, ini adalah solusi hackish, tetapi berfungsi untuk aliran tak terbatas

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

Sekarang Anda dapat membatasi streaming sesuai durasi yang Anda inginkan

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

PS Saya berharap ada solusi yang lebih baik, seperti clojure(partition 2 1 stream)


6
Kudos untuk menunjukkan bahwa kelas anonim terkadang merupakan alternatif yang berguna untuk lambda.
Aleksandr Dubinsky

2
@aepurniet Saya berasumsi itu tidak akan bekerja dengan benar. Menurut parallelStreamdokumen: "Untuk menjaga perilaku yang benar, parameter perilaku ini tidak boleh mengganggu, dan dalam banyak kasus harus tanpa kewarganegaraan"
mishadoff

14
Ini benar-benar bertentangan dengan desain kerangka aliran dan secara langsung melanggar kontrak API peta, karena fungsi anonim bukan tanpa kewarganegaraan. Coba jalankan ini dengan aliran paralel dan lebih banyak data sehingga kerangka aliran membuat lebih banyak thread yang berfungsi, dan Anda akan melihat hasilnya: "kesalahan" acak yang jarang terjadi hampir tidak mungkin direproduksi dan sulit dideteksi sampai Anda memiliki cukup data (dalam produksi?). Ini bisa menjadi bencana.
Mario Rossi

4
@AleksandrDubinsky Anda salah tentang limit / skip yang dapat diparalelkan; implementasi yang disediakan di JDK sebenarnya bekerja secara paralel. Karena operasi terikat untuk menghadapi pesanan, paralelisasi tidak selalu memberikan manfaat kinerja, tetapi dalam situasi Q tinggi, hal itu bisa.
Brian Goetz

4
@AleksandrDubins Salah. Ini mungkin melewatkan elemen acak jika sungai adalah unordered (rangka pertemuan telah ada ditetapkan, sehingga secara logis ada adalah tidak ada "pertama" atau "n" elemen, hanya elemen.) Namun apakah aliran tersebut memerintahkan atau unordered, lewati selalu mampu untuk bekerja secara paralel. Hanya ada sedikit paralelisme untuk diekstraksi jika aliran diurutkan, tetapi masih paralel.
Brian Goetz

15

Saya telah menerapkan pembungkus spliterator yang mengambil setiap nelemen Tdari spliterator asli dan menghasilkan 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();
    }
}

Metode berikut dapat digunakan untuk membuat aliran berurutan:

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

Penggunaan sampel:

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

Apakah itu mengulangi setiap elemen dua kali?
Aleksandr Dubinsky

Nggak. Ini menciptakan aliran baru yang berisi List<E>elemen. Setiap daftar berisi nelemen yang berurutan dari aliran asli. Periksa sendiri;)
Tomek Rękawek

Bisakah Anda mengubah jawaban Anda sehingga setiap elemen (kecuali yang pertama dan terakhir) diulang?
Aleksandr Dubinsky

4
+1 Saya rasa ini adalah pekerjaan yang baik dan harus digeneralisasikan ke ukuran langkah apa pun selain ukuran partisi. Ada banyak kebutuhan akan suatu (partition size step)fungsi dan ini tentang cara terbaik untuk mendapatkannya.
Marko Topolnik

3
Pertimbangkan menggunakan ArrayDequeuntuk kinerja, daripada LinkedList.
Marko Topolnik

14

Anda dapat melakukan ini dengan metode Stream.reduce () (Saya belum melihat jawaban lain menggunakan teknik ini).

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
Ini akan mengembalikan (1,2) (2,3) bukan (1,2) (3,4). Juga saya tidak yakin apakah itu akan diterapkan secara berurutan (tentu tidak ada jaminan itu).
Aleksandr Dubinsky

1
Silakan periksa pertanyaannya, itulah perilaku yang dimaksudkan @Aleksandr Dubinsky
SamTebbs33

3
Ahh, ya, maaf. Dan untuk berpikir, saya menulisnya.
Aleksandr Dubinsky


6

Anda dapat melakukan ini di cyclops-react (saya berkontribusi pada perpustakaan ini), menggunakan operator geser.

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

Atau

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

Dengan asumsi konstruktor Pair dapat menerima Koleksi dengan 2 elemen.

Jika Anda ingin mengelompokkan dengan 4, dan kenaikan 2 itu juga didukung.

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

Metode statis setara untuk membuat tampilan geser di atas java.util.stream.Stream juga disediakan di kelas StreamUtils cyclops-streams .

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

Catatan: - untuk operasi single-threaded, ReactiveSeq akan lebih sesuai. LazyFutureStream memperluas ReactiveSeq tetapi terutama ditujukan untuk penggunaan bersamaan / paralel (ini adalah Stream of Futures).

LazyFutureStream memperluas ReactiveSeq yang memperluas Seq dari jOOλ yang mengagumkan (yang memperluas java.util.stream.Stream), sehingga solusi yang diberikan Lukas juga akan berfungsi dengan salah satu jenis Stream. Bagi siapa pun yang tertarik, perbedaan utama antara operator jendela / geser adalah daya relatif / kompleksitas yang jelas trade off dan kesesuaian untuk digunakan dengan aliran tak terbatas (geser tidak menghabiskan aliran, tetapi menyangga saat mengalir).


Dengan cara ini Anda mendapatkan [(0,1) (2,3) ...] tetapi pertanyaannya menanyakan [(0,1) (1,2) ...]. Silakan lihat jawaban saya dengan RxJava ...
frhack

1
Anda benar, saya salah, saya salah membaca pertanyaan - operator geser adalah yang benar untuk digunakan di sini. Saya akan memperbarui jawaban saya - terima kasih!
John McClean

4

The proton-pack perpustakaan menyediakan functionnality berjendela. Diberikan kelas Pair dan Stream, Anda dapat melakukannya seperti ini:

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

Sekarang pairsaliran tersebut berisi:

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

Namun, sepertinya Anda perlu membuatnya stdua kali! Bisakah pustaka ini menyelesaikan masalah menggunakan aliran tunggal?
Aleksandr Dubinsky

@AleksandrDubinsky Saya rasa tidak tersedia dengan spliterator saat ini. Saya mengirimkan masalah github.com/poetix/protonpack/issues/9
Alexis C.

@AleksandrDubinsky windowedFungsinya telah ditambahkan! Lihat edit.
Alexis C.

1
Mengapa Anda tidak menghapus jawaban lama Anda, sehingga pengguna lain dapat melihat solusinya, bukan riwayat.
Aleksandr Dubinsky

4

Menemukan pasangan yang berurutan

Jika Anda ingin menggunakan pustaka pihak ketiga dan tidak membutuhkan paralelisme, maka jOOλ menawarkan fungsi jendela gaya SQL sebagai berikut

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

Menghasilkan

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

The lead()Fungsi mengakses nilai berikutnya dalam rangka traversal dari jendela.

Menemukan tripel / quadruples / n-tuple yang berurutan

Sebuah pertanyaan di komentar meminta solusi yang lebih umum, di mana tidak berpasangan tetapi n-tupel (atau mungkin daftar) harus dikumpulkan. Inilah pendekatan alternatif:

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

Menghasilkan daftar daftar

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

Tanpa itu filter(w -> w.count() == n), hasilnya pasti

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

Penafian: Saya bekerja untuk perusahaan di belakang jOOλ


Menarik. Bagaimana jika saya perlu mengelompokkan 3 atau lebih elemen? Gunakan w.lead().lead()?
Raul Santelices

1
@RaulSantelices: tuple(w.value(), w.lead(1), w.lead(2))akan menjadi pilihan. Saya telah memperbarui jawaban saya dengan solusi yang lebih umum untuklength = n
Lukas Eder

1
Saya mengerti benar bahwa .window()bukan operasi malas yang mengumpulkan seluruh aliran input menjadi beberapa koleksi perantara, kemudian membuat aliran baru darinya?
Tagir Valeev

@TagirValeev: Ya, itulah implementasi saat ini. Dalam kasus di atas (tidak Comparatordigunakan untuk menyusun ulang jendela), maka pengoptimalan seperti ini dapat dilakukan, dan kemungkinan besar akan diterapkan di masa mendatang.
Lukas Eder


2

Kita bisa menggunakan RxJava ( pustaka ekstensi reaktif yang sangat kuat )

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

The penyangga Operator mengubah sebuah diamati yang memancarkan item menjadi diamati yang memancarkan buffered koleksi barang-barang ..


1
Saya digunakan Observable.zip(obs, obs.skip(1), pair->{...})sampai sekarang! Saya tidak tahu Observable.buffermemiliki versi dengan langkah (dan saya terbiasa dengan ziptrik dari python). +1
Reut Sharabani

1

Operasi ini pada dasarnya stateful, jadi bukan apa yang harus diselesaikan oleh aliran - lihat bagian "Perilaku Tanpa Kewarganegaraan" di javadoc :

Pendekatan terbaik adalah menghindari parameter perilaku stateful untuk mengalirkan operasi seluruhnya

Salah satu solusinya di sini adalah memperkenalkan status dalam aliran Anda melalui penghitung eksternal, meskipun ini hanya akan berfungsi dengan aliran berurutan.

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

Pertanyaannya meminta untuk mengumpulkan elemen yang berurutan dari aliran masukan, tidak hanya untuk mengumpulkan bilangan bulat yang berurutan. Klarifikasi penting dari terminologi Stream:! = "Lambdas".
Aleksandr Dubinsky

Anda dapat mengganti AtomicInteger dengan AtomicReference. Alternatifnya adalah menggulung kolektor Anda sendiri atau menggunakan pustaka eksternal seperti dalam contoh ini: stackoverflow.com/a/30090528/829571
assylias

Lihat suntingan saya. Juga saya tidak yakin saya mengerti komentar Anda tentang lambda! = Stream. Jawaban lain yang menggunakan kelas anonim pada dasarnya melakukan hal yang sama kecuali bahwa status dipegang oleh kelas anonim alih-alih eksternal ...
assylias

1
Itu bekerja. The StreamExperpustakaan juga menemukan baik dan bisa menjadi jawaban sendiri. Komentar saya tentang "streams! = Lambdas" mengacu pada Anda yang menyatakan "Operasi pada dasarnya stateful jadi bukan apa yang lambda maksudkan untuk dipecahkan." Saya pikir Anda bermaksud menggunakan kata "aliran".
Aleksandr Dubinsky

Oh begitu - saya sudah mengklarifikasi itu.
assylias

0

Dalam kasus Anda, saya akan menulis IntFungsi kustom saya yang melacak int terakhir yang dilewati dan menggunakannya untuk memetakan IntStream asli.

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

  }

}

Masalah dengan ini adalah hal itu membuat keadaan tanpa kewarganegaraan keluar dari jendela.
Rob

@ Rob dan apa masalahnya dengan itu?
Aleksandr Dubinsky

Salah satu poin utama lambda adalah tidak memiliki status yang bisa berubah sehingga integrator internal dapat memparalelkan pekerjaan.
Rob

@Rob: Ya, Anda benar, tetapi aliran contoh yang diberikan menentang paralelisme karena setiap item (kecuali yang pertama dan terakhir) digunakan sebagai item pertama dan kedua dari beberapa pasangan.
jpvee

@jpvee ya saya pikir itu yang Anda pikirkan. Saya bertanya-tanya apakah tidak ada cara untuk melakukan ini dengan beberapa pembuat peta lain. Intinya, semua yang Anda perlukan akan setara dengan membuat penambah loop menjadi berpasangan lalu meminta functor mengambil 2 argumen. Itu pasti mungkin.
Rob

0

Untuk menghitung perbedaan berturut-turut dalam waktu (nilai-x) dari deret waktu, saya menggunakan metode stream's collect(...):

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

Di mana DifferenceCollector terlihat seperti ini:

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

Anda mungkin dapat memodifikasi ini agar sesuai dengan kebutuhan Anda.


0

Saya akhirnya menemukan cara untuk mengelabui Stream.reduce agar dapat menangani pasangan nilai dengan rapi; Ada banyak kasus penggunaan yang membutuhkan fasilitas ini yang tidak muncul secara alami di JDK 8:

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

Trik yang saya gunakan adalah return right; pernyataan.


1
Saya tidak berpikir reducemembuat jaminan yang cukup untuk bekerja.
Aleksandr Dubinsky

Akan tertarik untuk mengetahui lebih banyak tentang jaminan yang cukup . Bisakah Anda menjelaskan lebih lanjut? Mungkin ada alternatif di Jambu biji ... tapi saya terkendala dan tidak bisa menggunakannya.
Beezer

-1

Solusi elegan adalah menggunakan zip . Sesuatu seperti:

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

Ini cukup ringkas dan elegan, namun menggunakan daftar sebagai masukan. Sumber aliran tak terbatas tidak dapat diproses dengan cara ini.

Masalah lain (yang lebih merepotkan) adalah bahwa zip bersama dengan seluruh kelas Stream akhir-akhir ini telah dihapus dari API. Kode di atas hanya bekerja dengan b95 atau rilis yang lebih lama. Jadi dengan JDK terbaru saya akan mengatakan tidak ada solusi gaya FP yang elegan dan saat ini kami hanya bisa berharap bahwa dalam beberapa cara zip akan diperkenalkan kembali ke API.


Memang, ziptelah dihapus. Saya tidak ingat semua apa yang ada di Streamskelas, tapi beberapa hal yang telah bermigrasi menjadi metode statis pada Streamantarmuka, dan ada juga StreamSupportdan Stream.Builderkelas.
Stuart Marks

Betul sekali. Beberapa metode lain seperti concat atau iterate telah dipindahkan dan menjadi metode default di Stream. Sayangnya zip baru saja dihapus dari API. Saya mengerti alasan di balik pilihan ini (misalnya kurangnya Tuple) tetapi tetap saja itu adalah fitur yang bagus.
gadget

2
@gadget Apa hubungannya tupel zip? Alasan apa pun yang mungkin ditemukan tidak membenarkan pembunuhan zip.
Aleksandr Dubinsky

@AleksandrDubinsky Dalam banyak kasus, zip digunakan untuk menghasilkan koleksi Pairs / Tuple sebagai output. Mereka berargumen bahwa jika mereka menyimpan zip, orang akan meminta Tuple sebagai bagian dari JDK juga. Saya tidak akan pernah menghapus fitur yang ada.
gadget

-1

Ini masalah yang menarik. Apakah percobaan hybrid saya di bawah ada gunanya?

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

Saya yakin ini tidak cocok untuk pemrosesan paralel, dan karenanya dapat didiskualifikasi.


Pertanyaannya tidak meminta pemrosesan paralel, tetapi mengasumsikan bahwa kita hanya memiliki a Stream, bukan a List. Tentu saja, kami juga dapat mencabut iterator dari Stream, jadi ini mungkin solusi yang valid. Namun demikian, ini adalah pendekatan asli.
Aleksandr Dubinsky

-1

Seperti yang telah diamati orang lain, ada, karena sifat masalah, beberapa keadaan yang diperlukan.

Saya dihadapkan pada masalah serupa, di mana saya menginginkan apa yang pada dasarnya adalah fungsi LEAD Oracle SQL. Upaya saya untuk menerapkannya ada di bawah ini.

/**
 * 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

Anda dapat mencapainya dengan menggunakan antrian terbatas untuk menyimpan elemen yang mengalir melalui aliran (yang mendasarkan pada ide yang saya jelaskan secara rinci di sini: Apakah mungkin untuk mendapatkan elemen berikutnya di Stream? )

Contoh di bawah ini pertama kali mendefinisikan instance kelas BoundedQueue yang akan menyimpan elemen melalui aliran (jika Anda tidak menyukai gagasan untuk memperluas LinkedList, lihat tautan yang disebutkan di atas untuk pendekatan alternatif dan yang lebih umum). Nanti Anda tinggal menggabungkan dua elemen berikutnya ke dalam contoh Pair:

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

Saya setuju dengan @aepurniet tetapi peta Anda harus menggunakan mapToObj

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

1
Baik. Tapi ini hanya mengumpulkan pasangan bilangan bulat, bukan pasangan elemen aliran (jenis apa pun).
Aleksandr Dubinsky

-5

Jalankan forloop yang berjalan dari 0 ke length-1streaming Anda

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
dan di mana lambda bagian dari solusinya?
Olimpiu POP

Ini tidak terjadi ketika streaming tidak terbatas
mishadoff

@Olimpiu - Di mana Anda mendapatkan lambda? Saya membaca pertanyaan itu dua kali untuk memastikan saya tidak melewatkannya. Saya juga memeriksa riwayat pengeditan. Dan pertanyaannya tidak terkait dengannya.
jww
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.