Je joue avec des opérations fonctionnelles paresseuses dans Java SE 8, et je veux map
un index i
vers une paire / un tuple (i, value[i])
, puis filter
basé sur le deuxième value[i]
élément, et finalement ne sortir que les indices.
Dois-je encore souffrir ceci: Quel est l'équivalent de la paire C ++ <L, R> en Java? dans la nouvelle ère audacieuse des lambdas et des ruisseaux?
Mise à jour: j'ai présenté un exemple plutôt simplifié, qui a une solution soignée proposée par @dkatzel dans l'une des réponses ci-dessous. Cependant, il ne se généralise pas . Par conséquent, permettez-moi d'ajouter un exemple plus général:
package com.example.test;
import java.util.ArrayList;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
boolean [][] directed_acyclic_graph = new boolean[][]{
{false, true, false, true, false, true},
{false, false, false, true, false, true},
{false, false, false, true, false, true},
{false, false, false, false, false, true},
{false, false, false, false, false, true},
{false, false, false, false, false, false}
};
System.out.println(
IntStream.range(0, directed_acyclic_graph.length)
.parallel()
.mapToLong(i -> IntStream.range(0, directed_acyclic_graph[i].length)
.filter(j -> directed_acyclic_graph[j][i])
.count()
)
.filter(n -> n == 0)
.collect(() -> new ArrayList<Long>(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2))
);
}
}
Cela donne une sortie incorrecte[0, 0, 0]
qui correspond aux décomptes pour les trois colonnes qui sont toutes false
. Ce dont j'ai besoin, ce sont les indices de ces trois colonnes. La sortie correcte doit être [0, 2, 4]
. Comment puis-je obtenir ce résultat?
la source
AbstractMap.SimpleImmutableEntry<K,V>
depuis des années ... Mais de toute façon, au lieu de la cartographiei
à(i, value[i])
juste pour filtrer parvalue[i]
et cartographie Retour ài
: pourquoi ne pas simplement filtrer parvalue[i]
en premier lieu, sans la mise en correspondance?i
dans le flux. J'ai également besoinvalue[i]
des critères. C'est pourquoi j'ai besoin(i, value[i])
[0, 2, 4]
?Réponses:
MISE À JOUR: Cette réponse est en réponse à la question initiale, Java SE 8 a-t-il des paires ou des tuples? (Et implicitement, sinon, pourquoi?) L'OP a mis à jour la question avec un exemple plus complet, mais il semble que cela puisse être résolu sans utiliser aucun type de structure Pair. [Note d'OP: voici l'autre bonne réponse .]
La réponse courte est non. Vous devez soit lancer le vôtre, soit apporter l'une des nombreuses bibliothèques qui l'implémentent.
Avoir une
Pair
classe en Java SE a été proposé et rejeté au moins une fois. Consultez ce fil de discussion sur l'une des listes de diffusion OpenJDK. Les compromis ne sont pas évidents. D'une part, il existe de nombreuses implémentations Pair dans d'autres bibliothèques et dans le code d'application. Cela démontre un besoin, et l'ajout d'une telle classe à Java SE augmentera la réutilisation et le partage. D'un autre côté, avoir une classe Pair ajoute à la tentation de créer des structures de données compliquées à partir de paires et de collections sans créer les types et abstractions nécessaires. (C'est une paraphrase du message de Kevin Bourillion à partir de ce fil.)Je recommande à tout le monde de lire l'intégralité de ce fil de discussion. C'est remarquablement perspicace et n'a pas de flamage. C'est assez convaincant. Quand il a commencé, j'ai pensé, "Ouais, il devrait y avoir une classe Pair dans Java SE" mais au moment où le thread a atteint sa fin, j'avais changé d'avis.
Notez cependant que JavaFX a la classe javafx.util.Pair . Les API JavaFX ont évolué séparément des API Java SE.
Comme on peut le voir dans la question liée Quel est l'équivalent de la paire C ++ en Java?il y a un espace de conception assez grand autour de ce qui est apparemment une API si simple. Les objets doivent-ils être immuables? Devraient-ils être sérialisables? Devraient-ils être comparables? Le cours doit-il être final ou non? Faut-il ordonner les deux éléments? Doit-il être une interface ou une classe? Pourquoi s'arrêter aux paires? Pourquoi pas des triples, des quads ou des N-tuples?
Et bien sûr, il y a l'inévitable dénomination bikeshed pour les éléments:
Un gros problème qui a à peine été mentionné est la relation des paires avec les primitifs. Si vous avez une
(int x, int y)
donnée qui représente un point dans l'espace 2D, la représenter commePair<Integer, Integer>
consomme trois objets au lieu de deux mots de 32 bits. En outre, ces objets doivent résider sur le tas et entraîneront une surcharge GC.Il semblerait clair que, comme les Streams, il serait essentiel qu'il y ait des spécialisations primitives pour les Paires. Voulons-nous voir:
Même un
IntIntPair
nécessiterait toujours un objet sur le tas.Celles-ci rappellent bien sûr la prolifération des interfaces fonctionnelles dans le
java.util.function
package de Java SE 8. Si vous ne voulez pas d'une API surchargée, lesquelles laisseriez-vous de côté? Vous pourriez également soutenir que cela ne suffit pas et que des spécialisations, par exemple,Boolean
devraient également être ajoutées.Mon sentiment est que si Java avait ajouté une classe Pair il y a longtemps, cela aurait été simple, voire simpliste, et cela n'aurait pas satisfait la plupart des cas d'utilisation que nous envisageons actuellement. Considérez que si Pair avait été ajouté dans la période de temps JDK 1.0, il aurait probablement été mutable! (Regardez java.util.Date.) Est-ce que les gens auraient été satisfaits de cela? Je suppose que s'il y avait une classe Pair en Java, ce ne serait pas vraiment utile et tout le monde roulera toujours la sienne pour satisfaire ses besoins, il y aurait diverses implémentations Pair et Tuple dans des bibliothèques externes, et les gens continueraient à se disputer / discuter de la manière de réparer la classe Pair de Java. En d'autres termes, un peu au même endroit où nous en sommes aujourd'hui.
Pendant ce temps, des travaux sont en cours pour résoudre le problème fondamental, qui est une meilleure prise en charge dans la JVM (et éventuellement le langage Java) pour les types valeur . Consultez ce document sur l' état des valeurs . Il s'agit d'un travail préliminaire et spéculatif, et il ne couvre que les problèmes du point de vue de la JVM, mais il a déjà beaucoup de réflexion derrière cela. Bien sûr, il n'y a aucune garantie que cela entrera dans Java 9, ou jamais n'importe où, mais cela montre la direction actuelle de la réflexion sur ce sujet.
la source
Pair<T,U>
. Puisque les génériques doivent être de type référence. Toutes les primitives seront encadrées lors de leur stockage. Pour stocker des primitives, vous avez vraiment besoin d'une classe différente.valueOf
auraient dû être le seul moyen d'obtenir une instance encadrée. Mais ceux-ci sont là depuis Java 1.0 et ne valent probablement pas la peine d'être modifiés à ce stade.Pair
ouTuple
classe avec une méthode de fabrique créant les classes de spécialisation nécessaires (avec un stockage optimisé) de manière transparente en arrière-plan. En fin de compte, les lambdas font exactement cela: ils peuvent capturer un nombre arbitraire de variables de type arbitraire. Et maintenant l'image d'un support de langage permettant de créer la classe de tuple appropriée au moment de l'exécution déclenchée par uneinvokedynamic
instruction…invokedynamic
usine basée similaire à la création lambda, une telle modernisation ultérieure ne poserait aucun problème. À propos, les lambdas n'ont pas non plus d'identité. Comme indiqué explicitement, l'identité que vous pourriez percevoir aujourd'hui est un artefact de l'implémentation actuelle.Vous pouvez jeter un œil sur ces classes intégrées:
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
la source
SimpleImmutableEntry
garantit uniquement que les références stockées dans leEntry
ne changent pas, pas que les champs des objets liéskey
etvalue
(ou ceux des objets auxquels ils sont liés ) ne changent pas.Malheureusement, Java 8 n'a pas introduit de paires ou de tuples. Vous pouvez toujours utiliser org.apache.commons.lang3.tuple bien sûr (que j'utilise personnellement en combinaison avec Java 8) ou vous pouvez créer vos propres wrappers. Ou utilisez Maps. Ou des trucs comme ça, comme expliqué dans la réponse acceptée à cette question à laquelle vous avez lié.
MISE À JOUR: JDK 14 introduit les enregistrements comme fonction de prévisualisation. Ce ne sont pas des tuples, mais ils peuvent être utilisés pour sauver la plupart des mêmes problèmes. Dans votre exemple spécifique ci-dessus, cela pourrait ressembler à ceci:
Une fois compilé et exécuté avec JDK 14 (au moment de l'écriture, il s'agit d'une version à accès anticipé) à l'aide de l'
--enable-preview
indicateur, vous obtenez le résultat suivant:la source
Il semble que l'exemple complet puisse être résolu sans utiliser aucun type de structure Pair. La clé est de filtrer sur les index de colonne, avec le prédicat vérifiant la colonne entière, au lieu de mapper les index de colonne au nombre d'
false
entrées dans cette colonne.Le code qui fait cela est ici:
Il en résulte une sortie
[0, 2, 4]
dont je pense que le résultat correct demandé par l'OP.Notez également l'
boxed()
opération qui encadre lesint
valeurs dans desInteger
objets. Cela permet d'utiliser letoList()
collecteur préexistant au lieu d'avoir à écrire des fonctions de collecteur qui font la boxe elles-mêmes.la source
true
). En conséquence, j'accepterai votre autre réponse comme correcte, mais j'indiquerai également celle-ci! Merci beaucoup :)Vavr (anciennement appelé Javaslang) ( http://www.vavr.io ) fournit également des tuples (jusqu'à une taille de 8). Voici le javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .
Voici un exemple simple:
Pourquoi JDK lui-même n'est pas venu avec un type simple de tuples jusqu'à maintenant est un mystère pour moi. L'écriture de classes wrapper semble être une affaire de tous les jours.
la source
Depuis Java 9, vous pouvez créer des instances
Map.Entry
plus faciles qu'auparavant:Map.entry
renvoie un non modifiableEntry
et interdit les valeurs nulles.la source
Puisque vous ne vous souciez que des index, vous n'avez pas du tout besoin de mapper aux tuples. Pourquoi ne pas simplement écrire un filtre qui utilise les éléments de recherche dans votre tableau?
la source
Oui.
Map.Entry
peut être utilisé comme unPair
.Malheureusement, cela n'aide pas avec les flux Java 8 car le problème est que même si les lambdas peuvent prendre plusieurs arguments, le langage Java ne permet de renvoyer qu'une seule valeur (objet ou type primitif). Cela implique que chaque fois que vous avez un flux, vous vous retrouvez avec un seul objet de l'opération précédente. C'est un manque dans le langage Java, car si plusieurs valeurs de retour étaient prises en charge ET que les flux les prenaient en charge, nous pourrions avoir des tâches non triviales beaucoup plus agréables effectuées par les flux.
Jusque-là, il n'y a que peu d'utilité.
EDIT 2018-02-12: En travaillant sur un projet, j'ai écrit une classe d'assistance qui aide à gérer le cas particulier d'avoir un identifiant plus tôt dans le flux dont vous avez besoin ultérieurement, mais la partie de flux entre les deux ne le sait pas. Jusqu'à ce que je parvienne à le publier seul, il est disponible sur IdValue.java avec un test unitaire sur IdValueTest.java
la source
Eclipse Collections a
Pair
et toutes les combinaisons de paires primitives / objets (pour les huit primitives).La
Tuples
fabrique peut créer des instances dePair
, et laPrimitiveTuples
fabrique peut être utilisée pour créer toutes les combinaisons de paires primitives / objets.Nous les avons ajoutés avant la sortie de Java 8. Ils ont été utiles pour implémenter des itérateurs clé / valeur pour nos cartes primitives, que nous prenons également en charge dans toutes les combinaisons primitives / objets.
Si vous êtes prêt à ajouter la surcharge supplémentaire de la bibliothèque, vous pouvez utiliser la solution acceptée de Stuart et collecter les résultats dans une primitive
IntList
pour éviter la boxe. Nous avons ajouté de nouvelles méthodes dans Eclipse Collections 9.0 pour permettre laInt/Long/Double
création de collections à partir deInt/Long/Double
Streams.Remarque: je suis un committer pour les collections Eclipse.
la source