Étant donné un flux tel que { 0, 1, 2, 3, 4 }
,
comment puis-je le transformer le plus élégamment en une forme donnée:
{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }
(en supposant, bien sûr, que j'ai défini la classe Pair)?
Edit: Il ne s'agit pas strictement d'ints ou de flux primitifs. La réponse doit être générale pour un flux de tout type.
java
java-8
java-stream
Aleksandr Dubinsky
la source
la source
list.stream().map(i -> new Pair(i, i+1));
Map.Entry
comme classe Pair. (Certes, certains pourraient considérer que c'est un hack, mais utiliser une classe intégrée est pratique.)Réponses:
Ma bibliothèque StreamEx qui étend les flux standard fournit une
pairMap
méthode pour tous les types de flux. Pour les flux primitifs, cela ne change pas le type de flux, mais peut être utilisé pour effectuer certains calculs. L'utilisation la plus courante consiste à calculer les différences:Pour le flux d'objets, vous pouvez créer tout autre type d'objet. Ma bibliothèque ne fournit aucune nouvelle structure de données visible par l'utilisateur comme
Pair
(c'est la partie du concept de bibliothèque). Cependant, si vous avez votre proprePair
classe et que vous souhaitez l'utiliser, vous pouvez effectuer les opérations suivantes:Ou si vous en avez déjà
Stream
:Cette fonctionnalité est implémentée à l'aide d' un séparateur personnalisé . Il a une surcharge assez faible et peut bien se paralléliser. Bien sûr, cela fonctionne avec n'importe quelle source de flux, pas seulement une liste / un tableau d'accès aléatoire comme beaucoup d'autres solutions. Dans de nombreux tests, il fonctionne très bien. Voici un benchmark JMH où nous trouvons toutes les valeurs d'entrée précédant une valeur plus grande en utilisant différentes approches (voir cette question).
la source
StreamEx
implementsIterable
! Hourra!)Stream
dans unStreamEx
?StreamEx.of(stream)
. Il existe d'autres méthodes statiques pratiques pour créer le flux à partir deCollection
, tableauReader
, etc. Modification de la réponse.pairMap
commandé sur des flux séquentiels? En fait, j'aimerais avoir forPairsOrdered (), mais comme il n'y a pas de telle méthode, puis-je la simuler d'une manière ou d'une autre?stream.ordered().forPairs()
oustream().pairMap().forEachOrdered()
?pairMap
est l'opération intermédiaire avec la fonction de mappeur sans état non interférente, l'ordre n'est pas spécifié pour elle de la même manière que pour simplemap
. LeforPairs
n'est pas ordonné par spécification, mais les opérations non ordonnées sont de facto ordonnées pour les flux séquentiels. Ce serait bien si vous formuliez votre problème d'origine en tant que question de stackoverflow distincte pour fournir plus de contexte.La bibliothèque de flux Java 8 est principalement destinée à diviser les flux en plus petits morceaux pour un traitement parallèle, de sorte que les étapes de pipeline avec état sont assez limitées, et les actions telles que l'obtention de l'index de l'élément de flux actuel et l'accès aux éléments de flux adjacents ne sont pas prises en charge.
Un moyen typique de résoudre ces problèmes, avec certaines limitations, bien sûr, consiste à piloter le flux par des index et à compter sur le traitement des valeurs dans une structure de données à accès aléatoire comme une ArrayList à partir de laquelle les éléments peuvent être récupérés. Si les valeurs étaient dans
arrayList
, on pourrait générer les paires comme demandé en faisant quelque chose comme ceci:Bien sûr, la limitation est que l'entrée ne peut pas être un flux infini. Ce pipeline peut cependant être exécuté en parallèle.
la source
arrayList
) est en fait une collection, c'est pourquoi je ne l'ai pas marquée comme réponse. (Mais félicitations pour votre badge d'or!)Ce n'est pas élégant, c'est une solution hackish, mais fonctionne pour des flux infinis
Vous pouvez désormais limiter votre diffusion à la longueur souhaitée
PS j'espère qu'il y a une meilleure solution, quelque chose comme clojure
(partition 2 1 stream)
la source
parallelStream
doc: "Pour préserver un comportement correct, ces paramètres comportementaux doivent être non interférents, et dans la plupart des cas doivent être apatrides"J'ai implémenté un wrapper de séparateur qui prend tous les
n
élémentsT
du séparateur d'origine et produitList<T>
:La méthode suivante peut être utilisée pour créer un flux consécutif:
Exemple d'utilisation:
la source
List<E>
éléments. Chaque liste contientn
des éléments consécutifs du flux d'origine. Vérifiez-le vous-même;)(partition size step)
fonction et c'est la meilleure façon de l'obtenir.ArrayDeque
pour les performances, de préférence àLinkedList
.Vous pouvez le faire avec la méthode Stream.reduce () (je n'ai vu aucune autre réponse utilisant cette technique).
la source
Vous pouvez le faire dans cyclops-react (je contribue à cette bibliothèque), en utilisant l'opérateur glissant.
Ou
En supposant que le constructeur Pair peut accepter une Collection avec 2 éléments.
Si vous souhaitez grouper par 4 et incrémenter de 2, cela est également pris en charge.
Des méthodes statiques équivalentes pour créer une vue glissante sur java.util.stream.Stream sont également fournies dans la classe Cyclops -streams StreamUtils .
Remarque: - pour une opération monothread, ReactiveSeq serait plus approprié. LazyFutureStream étend ReactiveSeq mais est principalement conçu pour une utilisation simultanée / parallèle (c'est un Stream of Futures).
LazyFutureStream étend ReactiveSeq qui étend Seq du génial jOOλ (qui étend java.util.stream.Stream), de sorte que les solutions présentées par Lukas fonctionneraient également avec l'un ou l'autre type de Stream. Pour toute personne intéressée, les principales différences entre les opérateurs fenêtre / glissement sont le compromis évident puissance / complexité relative et l'aptitude à être utilisé avec des flux infinis (le glissement ne consomme pas le flux, mais des tampons lorsqu'il s'écoule).
la source
La bibliothèque proton-pack fournit la fonctionnalité fenêtrée. Étant donné une classe Pair et un Stream, vous pouvez le faire comme ceci:
Maintenant, le
pairs
flux contient:la source
st
deux fois! Cette bibliothèque peut-elle résoudre le problème en utilisant un seul flux?windowed
fonctionnalité a été ajoutée! Voir la modification.Trouver des paires successives
Si vous êtes prêt à utiliser une bibliothèque tierce et n'avez pas besoin de parallélisme, alors jOOλ propose des fonctions de fenêtre de style SQL comme suit
Céder
La
lead()
fonction accède à la valeur suivante dans l'ordre de parcours à partir de la fenêtre.Recherche de triples / quadruples / n-tuples successifs
Une question dans les commentaires demandait une solution plus générale, où non pas des paires mais des n-uplets (ou éventuellement des listes) devraient être collectés. Voici donc une approche alternative:
Produire une liste de listes
Sans le
filter(w -> w.count() == n)
, le résultat seraitClause de non-responsabilité: je travaille pour l'entreprise derrière jOOλ
la source
w.lead().lead()
?tuple(w.value(), w.lead(1), w.lead(2))
serait une option. J'ai mis à jour ma réponse avec une solution plus générique pourlength = n
.window()
n'est pas une opération paresseuse qui collecte tout le flux d'entrée dans une collection intermédiaire, puis crée un nouveau flux à partir de celui-ci?Comparator
utilisé pour les fenêtres Réorganiser), puis une optimisation comme cela serait possible, et est susceptible d'être mis en œuvre à l'avenir.Streams.zip(..)
est disponible à Guava , pour ceux qui en dépendent.Exemple:
la source
Nous pouvons utiliser RxJava ( bibliothèque d' extension réactive très puissante )
la source
Observable.zip(obs, obs.skip(1), pair->{...})
jusqu'à maintenant! Je ne savais pas qu'il yObservable.buffer
avait une version avec une étape (et je suis habitué à l'zip
astuce de python). +1L'opération est essentiellement avec état donc pas vraiment ce que les flux sont censés résoudre - voir la section "Comportements sans état" dans le javadoc :
Une solution ici est d'introduire un état dans votre flux via un compteur externe, bien que cela ne fonctionnera qu'avec un flux séquentiel.
la source
Stream
:! = "Lambdas".StreamEx
bibliothèque est également une bonne trouvaille et pourrait être une réponse en soi. Mon commentaire sur "streams! = Lambdas" fait référence à vous en déclarant "L'opération est essentiellement avec état donc pas vraiment ce que les lambdas sont censés résoudre." Je pense que vous vouliez utiliser le mot «flux».Dans votre cas, j'écrirais mon IntFunction personnalisé qui garde la trace du dernier int passé et l'utiliser pour mapper l'IntStream d'origine.
la source
Pour le calcul de différences successives dans le temps (valeurs x) d'une série chronologique, j'utilise la
stream
s »collect(...)
procédé:Où le DifferenceCollector est quelque chose comme ceci:
Vous pouvez probablement le modifier en fonction de vos besoins.
la source
J'ai finalement trouvé un moyen de tromper le Stream.reduce pour pouvoir gérer proprement des paires de valeurs; il existe une multitude de cas d'utilisation qui nécessitent cette fonctionnalité qui n'apparaît pas naturellement dans JDK 8:
L'astuce que j'utilise est le bon retour; déclaration.
la source
reduce
donne des garanties suffisantes pour que cela fonctionne.Une solution élégante serait d'utiliser zip . Quelque chose comme:
C'est assez concis et élégant, mais il utilise une liste comme entrée. Une source de flux infinie ne peut pas être traitée de cette manière.
Un autre problème (beaucoup plus gênant) est que le zip avec toute la classe Streams a été récemment supprimé de l'API. Le code ci-dessus ne fonctionne qu'avec b95 ou versions antérieures. Donc, avec le dernier JDK, je dirais qu'il n'y a pas de solution élégante de style FP et pour le moment, nous pouvons juste espérer que d'une manière ou d'une autre, zip sera réintroduit dans l'API.
la source
zip
été supprimé. Je ne me rappelle pas tout ce qui était sur laStreams
classe, mais certaines choses ont migré à être des méthodes statiques sur l'Stream
interface, et il y a aussiStreamSupport
etStream.Builder
classes.zip
? La raison pédante qui pourrait être inventée ne justifie pas le meurtrezip
.C'est un problème intéressant. Ma tentative hybride ci-dessous est-elle bonne?
Je pense qu'il ne se prête pas à un traitement parallèle et peut donc être disqualifié.
la source
Stream
, pas unList
. Bien sûr, nous pouvons également extraire un itérateur d'un Stream, donc cela pourrait être une solution valide. Néanmoins, c'est une approche originale.Comme d'autres l'ont observé, il y a, en raison de la nature du problème, un certain état requis.
J'ai été confronté à un problème similaire, dans lequel je voulais ce qui était essentiellement la fonction Oracle SQL LEAD. Ma tentative de mise en œuvre est ci-dessous.
la source
Vous pouvez y parvenir en utilisant une file d'attente limitée pour stocker les éléments qui circulent dans le flux (qui repose sur l'idée que j'ai décrite en détail ici: est-il possible d'obtenir l'élément suivant dans le flux? )
L'exemple Belows définit d'abord l'instance de la classe BoundedQueue qui stockera les éléments passant par le flux (si vous n'aimez pas l'idée d'étendre la LinkedList, reportez-vous au lien mentionné ci-dessus pour une approche alternative et plus générique). Plus tard, vous combinez simplement deux éléments suivants dans l'instance de Pair:
la source
Je suis d'accord avec @aepurniet mais à la place, vous devez utiliser mapToObj
la source
Exécutez une
for
boucle qui va de 0 àlength-1
de votre fluxla source