Зберіть послідовні пари з потоку


102

Даний потік, наприклад { 0, 1, 2, 3, 4 },

як я можу найелегантніше перетворити його на задану форму:

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

(якщо, звичайно, я визначив пару класу)?

Редагувати: мова йде не лише про вбудовані або примітивні потоки. Відповідь повинна бути загальною для потоку будь-якого типу.


2
Термін від FP - це "розділ", але я не знаходжу в Java методу з потрібною семантикою. Він має розділення на присудок.
Марко Топольник

1
Зазвичай сплітератор в JDK 8 вважається для переходу та перегородки. Я спробую також придумати приклад.
Olimpiu POP

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

2
Для еквівалентною не потоків питання, см stackoverflow.com/questions/17453022 / ...
Raedwald

До речі, деякі люди використовують будь-яку реалізацію Map.Entryяк клас пари. (Звичайно, деякі можуть вважати, що хак, але використання вбудованого класу зручно.)
Василь Бурк

Відповіді:


33

Моя бібліотека StreamEx, яка розширює стандартні потоки, забезпечує pairMapметод для всіх типів потоків. Для примітивних потоків він не змінює тип потоку, але може бути використаний для здійснення деяких обчислень. Найбільш поширене використання - це обчислення різниці:

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

Для потоку об'єктів ви можете створити будь-який інший тип об'єкта. Моя бібліотека не забезпечує будь-яких нових видимих ​​для користувача структур даних Pair(це частина концепції бібліотеки). Однак якщо у вас є власний Pairклас і ви хочете ним користуватися, ви можете зробити наступне:

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

Або якщо у вас вже є кілька Stream:

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

Ця функціональність реалізована за допомогою спеціального сплейтератора . Він має досить низькі накладні витрати і може добре паралелізувати. Звичайно, він працює з будь-яким джерелом потоку, а не лише зі списком / масивом випадкового доступу, як у багатьох інших рішень. У багатьох тестах він працює дуже добре. Ось показник JMH, де ми знаходимо всі вхідні значення, що передують більшому значенню, використовуючи різні підходи (див. Це питання).


Дякую! Чим більше я вивчаю цю бібліотеку, тим більше я її люблю. Можливо, я нарешті почну використовувати потоки. ( StreamExвтілює Iterable! Ура!)
Олександр Дубінський

Щоб ваша відповідь була 100% повною, ви могли б показати, як її загорнути Streamв StreamEx?
Олександр Дубінський

3
@AleksandrDubinsky: просто використовуй StreamEx.of(stream). Є й інші зручні статичні методи для створення потоку з Collection, масиву Readerтощо. Редагував відповідь.
Тагір Валєєв

@TagirValeev pairMapзамовлений у послідовних потоках? Насправді, я хотів би мати forPairsOrряд (), але оскільки такого методу немає, чи можу я його якось імітувати? stream.ordered().forPairs()або stream().pairMap().forEachOrdered()?
Аскар Каликов

1
@AskarKalykov, pairMapце проміжна операція з функцією відображення без перешкод без стану, впорядкування для неї не визначено так само, як для простого map. Не forPairsупорядкований специфікацією, але не упорядковані операції де-факто впорядковані для послідовних потоків. Було б добре, якщо ви сформулюєте свою оригінальну проблему як окреме запитання про stackoverflow, щоб забезпечити більше контексту.
Тагір Валєєв

74

Бібліотека потоків Java 8 в основному орієнтована на розбиття потоків на менші шматки для паралельної обробки, тому стадії стаціонарного конвеєра досить обмежені, і такі дії, як отримання індексу поточного елемента потоку та доступ до сусідніх елементів потоку, не підтримуються.

Типовим способом вирішення цих проблем, з певними обмеженнями, звичайно, є керування потоком за індексами та покладатися на те, що значення обробляються в деякій структурі даних з випадковим доступом, як ArrayList, з якої можна отримати елементи. Якщо значення були в arrayList, можна було б генерувати пари, як цього вимагають, роблячи щось подібне:

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

Звичайно обмеженням є те, що вхід не може бути нескінченним потоком. Однак цей трубопровід може проходити паралельно.


5
"Вхід не може бути нескінченним потоком." Власне, вхід взагалі не може бути потоком. Вхід ( arrayList) насправді є колекцією, тому я не позначив це як відповідь. (Але вітаємо з вашим золотим значком!)
Олександр Дубінський,

16

Це не елегантно, це хакітське рішення, але працює для нескінченних потоків

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

Тепер ви можете обмежити потік потрібною вами довжиною

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

PS Я сподіваюся, що є краще рішення, щось на зразок клоджура(partition 2 1 stream)


6
Кудос для того, щоб вказати, що анонімні класи - іноді корисна альтернатива лямбдам.
Олександр Дубінський

2
@aepurniet Я припускаю, що він не працюватиме правильно. За словами parallelStreamдоктора: "Щоб зберегти правильну поведінку, ці параметри поведінки повинні бути не заважаючими, і в більшості випадків повинні бути без громадянства"
помилка

14
Це повністю суперечить дизайну фреймворку потоків і прямо порушує договір API API, оскільки анонімна функція не є без громадянства. Спробуйте запустити це з паралельним потоком і більше даних, щоб структура потоку створювала більше робочих потоків, і ви побачите результат: рідкі випадкові "помилки" майже неможливо відтворити і важко виявити, поки у вас не буде достатньо даних (у виробництві?). Це може бути згубно.
Маріо Россі

4
@AleksandrDubinsky Ви неправі щодо паралелізації межі / пропуску; реалізація, передбачена JDK, насправді працює паралельно. Оскільки операція пов'язана з порядком зустрічі, паралелізація не завжди може принести користь для продуктивності, але в ситуаціях високого Q це може.
Брайан Гец

4
@AleksandrDubinsky Неправильно. Він може пропустити випадковий елемент , якщо потік невпорядкований (має не визначений порядок зустрічі, тому логічно там немає ніякого «перший» або «енний» елемент, тільки елементи.) Але чи є потік упорядкований або невпорядкований, пропускати завжди був в стані працювати паралельно. Існує лише менше паралелізму для вилучення, якщо потік замовлений, але його все одно паралельний.
Брайан Гец

15

Я реалізував оболонку spliterator, яка бере всі nелементи Tз оригінального сплітератора і виробляє 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();
    }
}

Для створення послідовного потоку може бути використаний наступний метод:

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

Використання зразка:

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

Чи повторює це кожен елемент двічі?
Олександр Дубінський

Ні. Він створює новий потік, що містить List<E>елементи. Кожен список містить nпослідовні елементи з початкового потоку. Перевірте самі;)
Tomek Rękawek

Чи можете ви змінити свою відповідь, щоб кожен елемент (крім першого та останнього) повторювався?
Олександр Дубінський

4
+1 Я думаю, що це хороша робота і її слід узагальнити до будь-якого розміру кроку на додаток до розміру розділу. Існує велика потреба у (partition size step)функції, і це приблизно найкращий спосіб її отримати.
Марко Топольник

3
Подумайте про використання ArrayDequeдля продуктивності, надати перевагу LinkedList.
Марко Топольник

14

Це можна зробити методом Stream.reduce () (я не бачив жодної відповіді за допомогою цієї методики).

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) (2,3) замість (1,2) (3,4). Також я не впевнений, чи застосовуватиметься це в порядку (звичайно, гарантії цього немає).
Олександр Дубінський

1
Будь ласка, перевірте питання, що передбачає поведінка @Aleksandr Dubinsky
SamTebbs33

4
Ах, так, вибачте. І подумати, я це написав.
Олександр Дубінський


6

Це можна зробити за допомогою циклоп-реакції (я сприяю цій бібліотеці), використовуючи оператор ковзання.

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

Або

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

Якщо припустити, що конструктор Pair може прийняти колекцію з 2 елементами.

Якщо ви хотіли згрупувати по 4, а приріст на 2, що також підтримується.

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

Еквівалентні статичні методи створення ковзного виду по java.util.stream.Stream також передбачені в циклоп-потоках класу StreamUtils .

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

Примітка: - для однопотокової роботи ReakicaSeq було б більш доцільним. LazyFutureStream розширює ReactiveSeq, але в першу чергу орієнтований на паралельне / паралельне використання (це Потік майбутнього).

LazyFutureStream розширює ReactiveSeq, що розширює Seq від дивовижного jOOλ (який розширює java.util.stream.Stream), тому рішення подарунків Лукаша також будуть працювати з будь-яким типом Stream. Для всіх, хто цікавиться первинними відмінностями між операторами вікна / ковзання є очевидна відносна потужність / складність відключення та придатність для використання з нескінченними потоками (ковзання не споживає потік, а буфери, коли він тече).


Таким чином ви отримуєте [(0,1) (2,3) ...], але питання задає [(0,1) (1,2) ...]. Будь ласка, дивіться мою відповідь з RxJava ...
frhack

1
Ви маєте рацію, моя погана, я неправильно перечитав питання - оператор ковзання є правильним для використання тут. Я оновлю свою відповідь - дякую!
Джон МакКлін

4

Бібліотека proton-pack надає віконну функціональність. Враховуючи клас пари та потік, ви можете це зробити так:

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

Тепер pairsпотік містить:

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

Однак, схоже, вам потрібно створити stдвічі! Чи може ця бібліотека вирішити проблему за допомогою одного потоку?
Олександр Дубінський

@AleksandrDubinsky Я не думаю, що це доступно для поточних сплейтерів. Я подав випуск github.com/poetix/protonpack/isissue/9
Алексіс К.

@AleksandrDubinsky windowedФункціональність додана! Дивіться редагування.
Алексіс С.

1
Чому б не видалити стару відповідь, щоб інші користувачі могли бачити рішення, а не історію.
Олександр Дубінський

4

Пошук послідовних пар

Якщо ви готові використовувати сторонні бібліотеки і вам не потрібен паралелізм, тоді jOOλ пропонує віконні функції у стилі SQL наступним чином

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

Врожайність

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

lead()Функція отримує доступ наступного значення в порядку обходу з вікна.

Знаходження послідовних трійків / чотирьох / n-кортежів

Питання в коментарях пропонувало більш загальне рішення, де не слід збирати пари, а n-кортежі (або, можливо, списки). Ось таким чином альтернативний підхід:

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

Вихід списку

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

Без цього filter(w -> w.count() == n)результат був би

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

Відмова: Я працюю в компанії, що стоїть за jOOλ


Цікаво. Що робити, якщо мені потрібно згрупувати 3 чи більше елементів? Використовувати w.lead().lead()?
Рауль Сантеліс

1
@RaulSantelices: tuple(w.value(), w.lead(1), w.lead(2))буде варіантом. Я оновив свою відповідь більш загальним рішенням дляlength = n
Лукаша Едера

1
Я правильно розумію, що .window()це не лінива операція, яка збирає весь вхідний потік у якусь проміжну колекцію, а потім створює з неї новий потік?
Тагір Валєєв

@TagirValeev: Так, це поточна реалізація. Чи не для наведеного вище випадку (Не Comparatorвикористовується для зміни порядку вікон), то оптимізація , як це було б можливо, і, ймовірно, буде реалізований в майбутньому.
Лукас Едер


2

Ми можемо використовувати RxJava (дуже потужна реактивна бібліотека розширень )

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

Буфера оператор перетворює спостережуваний , який випромінює предмети в спостережуваний , який випромінює буферном колекції цих елементів ..


1
Я використовував Observable.zip(obs, obs.skip(1), pair->{...})до цього часу! Я не знав, чи Observable.bufferбула версія з кроком (і я звикла до zipхитрості від python). +1
Reut Sharabani

1

Операція по суті є державною, тому насправді не те, які потоки мають вирішити - дивіться розділ "Поведінки без громадянства" в javadoc :

Найкращий підхід - уникнути стаціонарних параметрів поведінки, щоб повністю передавати операції

Одне тут рішення - ввести стан у потоці через зовнішній лічильник, хоча воно буде працювати лише з послідовним потоком.

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

Питання вимагає зібрати послідовні елементи вхідного потоку, а не просто збирати послідовні цілі числа. Важливе уточнення термінології Stream:! = "Лямбда".
Олександр Дубінський

Ви можете замінити AtomicInteger AtomicReference. В якості альтернативи можна згорнути свій власний колектор або використовувати зовнішні бібліотеки , такі як в цьому прикладі: stackoverflow.com/a/30090528/829571
assylias

Дивіться мою редакцію. Також я не впевнений, що я розумію ваш коментар до лямбда! = Stream. Інша відповідь, що використовує анонімний клас, робить по суті те ж саме, за винятком того, що стан тримає анонімний клас, а не зовнішній ...
assylias

1
Це працює. StreamExБібліотека також хороша знахідка , і може бути відповідь сам по собі. Мій коментар до "потоків! = Лямбдас" стосується вас, заявивши, що "Операція за своєю суттю є релевантною, тому насправді не те, що мають бути вирішені лямбди". Я думаю, ти мав на увазі використовувати слово "потоки".
Олександр Дубінський

О, бачу - я це уточнив.
Ассілія

0

У вашому випадку я б написав свій власний IntFunction, який відслідковує останній переданий int, і використовувати його для відображення оригінальної IntStream.

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

  }

}

Проблема в цьому полягає в тому, що він викидає без громадянства у вікно.
Роб

@Rob і в чому проблема?
Олександр Дубінський

Один з головних моментів лямбда - це не мати змінного стану, щоб внутрішні інтегратори могли паралелізмувати роботу.
Роб

@Rob: Так, ви маєте рацію, але даний приклад потоку все одно не відповідає паралелізму, оскільки кожен елемент (крім першого та останнього) використовується як перший та другий елемент якоїсь пари.
jpvee

@jpvee так, я зрозумів, що це ти думав. Мені цікаво, якщо немає способу зробити це з іншим картографом. По суті, все, що вам знадобилося б, було б еквівалентом того, щоб змусити інкрементатор циклу перейти на двійки, тоді функтор повинен взяти 2 аргументи. Це повинно бути можливим.
Роб

0

Для обчислення послідовних різниць у часі (значень x) часового ряду я використовую метод streams collect(...):

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

Де DifferenceCollector є приблизно таким:

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

Можливо, ви могли б змінити це відповідно до ваших потреб.


0

Нарешті я придумав спосіб обмацувати Stream.reduce, щоб можна було акуратно розібратися з парами значень; Є безліч випадків використання, які вимагають цього засобу, який, природно, не з’являється в 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;
}

Трюк, який я використовую, - це повернути право; заява.


1
Я не думаю, що reduceдає достатні гарантії для цього.
Олександр Дубінський

Було б цікаво дізнатися більше про достатні гарантії . Чи можете ви, будь ласка, докладно? Можливо, є альтернатива в Гуаві ... але я обмежений і не можу її використати.
Beezer

-1

Елегантним рішенням буде використання блискавки . Щось на зразок:

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

Це досить стисло та елегантно, проте він використовує список як вхідний текст. Нескінченне джерело потоку не може бути оброблене таким чином.

Ще одне (набагато більш клопітке) питання полягає в тому, що поштовий індекс разом із усім класом Streams останнім часом видалено з API. Вищевказаний код працює лише з версіями b95 або старішими. Отже, з останнім JDK я б сказав, що не існує елегантного рішення у стилі FP, і зараз ми можемо просто сподіватися, що якийсь поштовий індекс буде знову введений в API.


Дійсно, zipзняли. Я не пам’ятаю всього того, що було на Streamsуроці, але деякі речі перейшли на статичні методи в Streamінтерфейсі, є також StreamSupportі Stream.Builderкласи.
Стюарт відзначає

Це вірно. Деякі інші методи, такі як concat або iterate, були переміщені та стали методами за замовчуванням у Stream. На жаль, zip щойно видалили з API. Я розумію причини цього вибору (наприклад, відсутність кортежів), але все-таки це була приємна особливість.
гаджет

2
@gadget Які стосуються кортежів zip? Яка б педантична причина не була придумана, це не виправдовує вбивство zip.
Олександр Дубінський

@AleksandrDubinsky У більшості випадків zip використовується для створення колекції пар / кортежів як вихід. Вони стверджували, що якщо вони зберігатимуть поштовий індекс, люди також просять Тупалів у складі JDK. Я б ніколи не видаляв існуючу функцію.
гаджет

-1

Це цікава проблема. Моя гібридна спроба нижче якоїсь корисної?

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

Я вважаю, що вона не піддається паралельній обробці, а отже, може бути дискваліфікована.


Питання не задавало паралельної обробки, але припускало, що ми маємо лише a Stream, а не a List. Звичайно, ми можемо також вивести ітератор із потоку, тому це може бути правильним рішенням. Тим не менш, це оригінальний підхід.
Олександр Дубінський

-1

Як зауважили інші, в силу характеру проблеми потрібна деяка прискіпливість.

Я зіткнувся з подібною проблемою, в якій хотів, що по суті є функцією Oracle SQL LEAD. Моя спроба здійснити це нижче.

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

Ви можете досягти цього, використовуючи обмежену чергу для зберігання елементів, що протікають через потік (що ґрунтується на ідеї, яку я детально описав тут: Чи можливо отримати наступний елемент в потоці? )

Нижче на прикладі спочатку визначається екземпляр класу BoundedQueue, який буде зберігати елементи, що проходять через потік (якщо вам не подобається ідея розширення LinkedList, зверніться до вищезазначеного посилання для альтернативного та більш загального підходу). Пізніше ви просто комбінуєте два наступні елементи в екземпляр 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

Я погоджуюся з @aepurniet, але замість цього вам потрібно використовувати mapToObj

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

1
Правильно. Але це просто збирає пари цілих чисел, а не пари елементів потоку (будь-якого типу).
Олександр Дубінський

-5

Запустіть forцикл, який працює від 0 до length-1вашого потоку

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
а де лямбда частина розчину?
Olimpiu POP

Це не той випадок, коли потік нескінченний
mishadoff

@Olimpiu - Де ти взяв лямбда, це була вимога? Я прочитав питання два рази, щоб переконатися, що я його не пропустив. Я також перевіряв історію правок. І питання не позначене цим.
jww
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.