110 votes

Java 8: Recueillir des paires successives à partir d'un flux

Compte tenu d'un flux comme { 0, 1, 2, 3, 4 },

comment puis-je le plus élégamment la transformer en forme:

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

(en supposant, bien sûr, j'ai la classe définie par Paire)?

Edit: Ce n'est pas strictement sur ints ou primitif ruisseaux. Une réponse pour objet de flux est très bien.

79voto

Stuart Marks Points 8927

Java 8 flux de la bibliothèque est principalement orienté vers le fractionnement du flux dans de plus petits morceaux pour le traitement en parallèle, de manière dynamique les étapes du pipeline sont assez limitées, et de faire les choses comme obtenir l'index de l'actuel flux d'élément et d'accéder aux cours d'eau adjacents éléments ne sont pas pris en charge.

D'une manière typique pour résoudre ces problèmes, avec certaines limites, bien sûr, est de conduire le flux par les index et compter sur d'avoir les valeurs en cours de traitement dans certains random-access structure de données comme une liste de tableaux dont les éléments peuvent être récupérés. Si les valeurs étaient en arrayList, on peut générer les paires comme l'a demandé de faire quelque chose comme ceci:

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

Bien sûr, la limitation est que l'entrée ne peut pas être un courant infini. Ce pipeline peuvent être exécutées en parallèle.

17voto

mishadoff Points 6034

Il n'est pas élégant et hackish solution, mais travaille pour les flux infinis

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

Maintenant, vous pouvez limiter les flux aussi longtemps que vous le souhaitez

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

P. S. j'espère qu'il y est une meilleure solution, quelque chose comme clojure (partition 2 1 stream)

15voto

Tomek Rękawek Points 4101

J'ai mis en place un spliterator wrapper qui prend chaque n éléments T de l'original spliterator et produit 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 LinkedList<>();
        this.dequeConsumer = new Consumer<T>() {
            @Override
            public void accept(T t) {
                deque.addLast(t);
            }
        };
    }

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

Méthode suivante peut être utilisée pour créer un consécutives de flux:

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

Exemple d'utilisation:

consecutiveStream(Stream.of(0, 1, 2, 3, 4, 5), 2).map(
    new Function<List<Integer>, Pair>() {
        public Pair apply(List<Integer> list) {
            return new Pair(list.get(0), list.get(1));
        }
    }).forEach(System.out::println);

1voto

assylias Points 102015

L'opération est essentiellement dynamique, donc pas vraiment ce qu'elles sont censées résoudre à voir le "Apatrides Comportements" dans la section de javadoc:

La meilleure approche est d'éviter dynamique comportementale paramètres de flux entièrement les opérations de

Une seule solution ici est d'introduire dans vos lambda par l'intermédiaire d'un compteur, bien qu'il ne fonctionne qu'avec un flux séquentiel.

public static void main(String[] args) {
    Stream<Integer> numbers = IntStream.rangeClosed(0, 4).boxed();
    AtomicInteger counter = new AtomicInteger(Integer.MIN_VALUE);
    List<Pair> collect = numbers.map(n -> {
                            int i = counter.getAndSet(n);
                            return i == Integer.MIN_VALUE ? null : new Pair(i, n);
                        })
                        .filter(p -> p != null)
                        .collect(toList());
    System.out.println(collect);
}

Pour référence:

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

0voto

jpvee Points 486

Dans votre cas, je voudrais écrire ma coutume IntFunction qui garde la trace de la dernière int passé et de l'utiliser pour la carte d'origine 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

  }

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X