Est-ce que Java SE 8 a des paires ou des tuples?

Je joue avec des opérations fonctionnelles paresseuses dans Java SE 8, et je veux map un index i sur une paire / un tuple (i, value[i]) , puis filter sur la base de l’élément deuxième value[i] et enfin sortir juste les indices.

Dois-je encore souffrir ceci: Quel est l’équivalent de la paire C ++ en Java? dans la nouvelle ère audacieuse des lambdas et des ruisseaux?

Mise à jour: J’ai présenté un exemple plutôt simplifié, qui propose une solution intéressante proposée par @dkatzel dans l’une des réponses ci-dessous. Cependant, cela ne se généralise pas . Permettez-moi donc d’append 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(Ssortingng[] 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(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2)) ); } } 

Cela donne une sortie incorrecte de [0, 0, 0] qui correspond aux comptages pour les trois colonnes qui sont toutes false . Ce dont j’ai besoin, ce sont les indices de ces trois colonnes. La sortie correcte devrait être [0, 2, 4] . Comment puis-je obtenir ce résultat?

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 pas?) 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 de paire. [Note de OP: voici l’autre réponse correcte .]


La réponse courte est non. Vous devez soit rouler votre propre ou apporter l’une des bibliothèques qui l’implémentent.

Avoir une classe Pair dans Java SE a été proposé et rejeté au moins une fois. Voir 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 de paires 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 les abstractions nécessaires. (C’est une paraphrase du message de Kevin Bourillion de ce sujet.)

Je recommande à tout le monde de lire l’intégralité du fil de discussion. C’est remarquablement perspicace et sans flammes. 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 est arrivé à 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 sur la question liée Qu’est-ce que l’équivalent de la paire C ++ en Java? Il existe un espace de conception assez vaste autour de ce qui est apparemment une API aussi simple. Les objects doivent-ils être immuables? Devraient-ils être sérialisables? Devraient-ils être comparables? La classe devrait-elle être finale ou non? Les deux éléments doivent-ils être commandés? Devrait-il s’agir d’une interface ou d’une classe? Pourquoi s’arrêter à deux? Pourquoi pas sortingples, quads ou N-tuples?

Et bien sûr, il y a l’incontournable nom des bikeshed pour les éléments:

  • (un B)
  • (première seconde)
  • (gauche droite)
  • (voiture, cdr)
  • (foo, bar)
  • etc.

La relation entre les paires et les primitives est un gros problème. Si vous avez un datum (int x, int y) qui représente un point dans un espace 2D, le représentant sous la forme Pair consum trois objects au lieu de deux mots de 32 bits. De plus, ces objects doivent résider sur le tas et entraîner des frais généraux de GC.

Il semblerait évident que, comme pour Streams, il serait essentiel que les paires se spécialisent. Voulons-nous voir:

 Pair ObjIntPair ObjLongPair ObjDoublePair IntObjPair IntIntPair IntLongPair IntDoublePair LongObjPair LongIntPair LongLongPair LongDoublePair DoubleObjPair DoubleIntPair DoubleLongPair DoubleDoublePair 

Même un IntIntPair nécessiterait toujours un object sur le tas.

Celles-ci, bien sûr, rappellent la prolifération des interfaces fonctionnelles dans le package java.util.function de Java SE 8. Si vous ne voulez pas d’une API gonflée, quelles sont celles que vous ometsortingez? Vous pourriez également faire valoir que cela ne suffit pas, et que des spécialisations pour, 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 JDK 1.0, il aurait probablement été mutable! (Regardez java.util.Date.) Est-ce que les gens auraient été heureux avec ça? J’imagine que si Java était une classe Pair, ce ne serait pas vraiment utile et tout le monde continuerait à déployer ses efforts pour satisfaire ses besoins, il y aurait plusieurs implémentations Pair et Tuple dans des bibliothèques externes. et les gens se disputeraient encore et discuteraient de la manière de réparer la classe Pair de Java. En d’autres termes, en quelque sorte au même endroit où nous sums aujourd’hui.

Pendant ce temps, des travaux sont en cours pour résoudre le problème fondamental, à savoir un meilleur support de la JVM (et éventuellement du langage Java) pour les types de valeur . Voir le document État des valeurs . Il s’agit d’un travail préliminaire et spéculatif qui ne couvre que les questions du sharepoint vue de la JVM, mais il a déjà une bonne reflection. Bien sûr, il n’ya aucune garantie que cela entre dans Java 9, ou n’y parvienne jamais, mais cela montre l’orientation actuelle de la reflection sur ce sujet.

Vous pouvez regarder ces classes intégrées:

  • AbstractMap.SimpleEntry
  • AbstractMap.SimpleImmutableEntry

Il semble que l’exemple complet puisse être résolu sans l’utilisation d’aucune structure de paire. La clé consiste à filtrer les index de colonne, avec le prédicat vérifiant la colonne entière, au lieu de mapper les index de colonne au nombre de false entrées dans cette colonne.

Le code qui fait cela est ici:

  System.out.println( IntStream.range(0, acyclic_graph.length) .filter(i -> IntStream.range(0, acyclic_graph.length) .noneMatch(j -> acyclic_graph[j][i])) .boxed() .collect(toList())); 

Cela se traduit par une sortie de [0, 2, 4] qui est, je pense, le résultat correct demandé par l’OP.

Notez également l’opération boxed() qui encadre les valeurs int dans les objects Integer . Cela permet d’utiliser le collecteur toList() préexistant à la place de devoir écrire les fonctions de collecteur qui font la boxe eux-mêmes.

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 des cartes. Ou des trucs comme ça, comme cela est expliqué dans la réponse acceptée à cette question à laquelle vous êtes lié.

Comme vous ne vous souciez que des index, vous n’avez pas besoin de mapper les tuples. Pourquoi ne pas simplement écrire un filtre qui utilise les éléments de recherche dans votre tableau?

  int[] value = ... IntStream.range(0, value.length) .filter(i -> value[i] > 30) //or whatever filter you want .forEach(i -> System.out.println(i)); 

Vavr (anciennement appelé Javaslang) ( http://www.vavr.io ) fournit également des tuples (jusqu’à 8). Voici le javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .

Voici un exemple simple:

 Tuple2 entry = Tuple.of(1, "A"); Integer key = entry._1; Ssortingng value = entry._2; 

Pourquoi JDK lui-même n’est pas venu avec un simple type de tuples, c’est un mystère pour moi. L’écriture de classes wrapper semble être une activité quotidienne.

Oui.

Map.Entry peut être utilisé comme une Pair .

Malheureusement, cela n’aide pas les stream Java 8 car le problème est que même si lambdas peut prendre plusieurs arguments, le langage Java ne permet que de renvoyer une seule valeur (object ou type primitif). Cela implique que chaque fois que vous avez un stream, vous obtenez un seul object de l’opération précédente. C’est un manque dans le langage Java, car si plusieurs valeurs de retour étaient supscopes, les stream AND les supportaient, nous pourrions avoir des tâches non sortingviales beaucoup plus intéressantes effectuées par les stream.

Jusque-là, il y a peu d’utilisation.

EDIT 2018-02-12: Pendant que je travaillais sur un projet, j’ai écrit une classe d’assistance qui aide à gérer le cas particulier d’un identifiant plus tôt dans le stream dont vous avez besoin ultérieurement, mais le stream intermédiaire ne le sait pas. Jusqu’à ce que je sois capable de le publier seul, il est disponible à IdValue.java avec un test unitaire à IdValueTest.java

Eclipse Collections a Pair et toutes les combinaisons de paires primitive / object (pour les huit primitives).

La fabrique de Tuples peut créer des instances de Pair , et la fabrique PrimitiveTuples peut être utilisée pour créer toutes les combinaisons de paires primitive / object.

Nous les avons ajoutés avant la sortie de Java 8. Ils ont été utiles pour implémenter des iterators clé / valeur pour nos cartes primitives, que nous prenons également en charge dans toutes les combinaisons primitives / objects.

Si vous souhaitez append la surcharge de la bibliothèque, vous pouvez utiliser la solution acceptée par 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 la création de collections Int/Long/Double stream Int/Long/Double .

 IntList list = IntLists.mutable.withAll(intStream); 

Note: Je suis un committer pour les collections Eclipse.