new - ¿Java SE 8 tiene pares o tuplas?
new icon java (8)
Como solo le importan los índices, no necesita asignarlos a tuplas. ¿Por qué no simplemente escribir un filtro que utiliza los elementos de búsqueda en su matriz?
int[] value = ...
IntStream.range(0, value.length)
.filter(i -> value[i] > 30) //or whatever filter you want
.forEach(i -> System.out.println(i));
Estoy jugando con las operaciones funcionales perezosas en Java SE 8, y quiero map
un índice i
a un par / tupla (i, value[i])
, luego filter
función del segundo elemento de value[i]
, y finalmente dar salida solo los índices.
¿Debo seguir sufriendo esto? ¿Cuál es el equivalente del par C ++ <L, R> en Java? en la nueva y audaz era de lambdas y arroyos?
Actualización: presenté un ejemplo bastante simplificado, que tiene una solución ordenada ofrecida por @dkatzel en una de las respuestas a continuación. Sin embargo, no generaliza. Por lo tanto, permítanme agregar un ejemplo más general:
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))
);
}
}
Esto da salida incorrecta de [0, 0, 0]
que corresponde a los recuentos de las tres columnas que son todas false
. Lo que necesito son los índices de estas tres columnas. La salida correcta debería ser [0, 2, 4]
. ¿Cómo puedo obtener este resultado?
Eclipse Collections tiene Pair
y todas las combinaciones de pares primitivos / objetos (para las ocho primitivas).
La fábrica Tuples
puede crear instancias de Pair
, y la fábrica PrimitiveTuples
se puede usar para crear todas las combinaciones de pares primitivos / objetos.
Añadimos esto antes de que se lanzara Java 8. Fueron útiles para implementar Iteradores de clave / valor para nuestros mapas primitivos, que también admitimos en todas las combinaciones primitivas / de objeto.
Si está dispuesto a agregar la sobrecarga de la biblioteca adicional, puede usar la solución aceptada de Stuart y recopilar los resultados en una IntList
primitiva para evitar el boxeo. Agregamos nuevos métodos en Eclipse Collections 9.0 para permitir la creación de colecciones Int/Long/Double
desde Int/Long/Double
Streams.
IntList list = IntLists.mutable.withAll(intStream);
Nota: soy un committer para las colecciones de Eclipse.
Parece que el ejemplo completo se puede resolver sin el uso de ningún tipo de estructura de pares. La clave es filtrar en los índices de columna, con el predicado comprobando la columna completa, en lugar de asignar los índices de columna al número de entradas false
en esa columna.
El código que hace esto está aquí:
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()));
Esto da como resultado una salida de [0, 2, 4]
que creo que es el resultado correcto solicitado por el OP.
También tenga en cuenta la operación en boxed()
que encapsula los valores int
en objetos Integer
. Esto le permite a uno usar el toList()
preexisted toList()
lugar de tener que escribir las funciones del recopilador que hacen el boxeo ellos mismos.
Puedes echar un vistazo a estas clases integradas:
Sí.
Map.Entry
se puede usar como un Pair
.
Desafortunadamente no ayuda con las secuencias de Java 8 ya que el problema es que aunque lambdas puede tomar múltiples argumentos, el lenguaje de Java solo permite devolver un único valor (objeto o tipo primitivo). Esto implica que cada vez que tenga una secuencia, terminará pasando un único objeto de la operación anterior. Esta es una falta en el lenguaje Java, porque si se admitieran múltiples valores devueltos Y si las secuencias lo admitieran, podríamos realizar tareas no triviales mucho más agradables realizadas por las transmisiones.
Hasta entonces, solo hay un pequeño uso.
EDITAR 2018-02-12: Mientras trabajaba en un proyecto, escribí una clase de ayuda que me ayuda a manejar el caso especial de tener un identificador antes en la transmisión que necesita en un momento posterior, pero la parte de transmisión en el medio no lo sabe. Hasta que llegue a liberarlo solo, está disponible en IdValue.java con una prueba unitaria en IdValueTest.java
Tristemente, Java 8 no introdujo pares o tuplas. Siempre puedes usar org.apache.commons.lang3.tuple por supuesto (que personalmente uso en combinación con Java 8) o puedes crear tus propios wrappers. O usa Maps. O cosas así, como se explica en la respuesta aceptada a la pregunta a la que se vinculó.
ACTUALIZACIÓN: Esta respuesta responde a la pregunta original: ¿Java SE 8 tiene Pares o Tuplas? (E implícitamente, si no, ¿por qué no?) El OP ha actualizado la pregunta con un ejemplo más completo, pero parece que se puede resolver sin utilizar ningún tipo de estructura de pares. [Nota de OP: esta es la otra respuesta correcta .]
La respuesta corta es no. O bien tienes que hacer tu propia o traer una de las varias bibliotecas que la implementa.
Tener una clase Pair
en Java SE fue propuesta y rechazada al menos una vez. Vea este hilo de discusión en una de las listas de correo de OpenJDK. Las compensaciones no son obvias. Por un lado, hay muchas implementaciones de Pair en otras bibliotecas y en el código de la aplicación. Eso demuestra una necesidad, y agregar dicha clase a Java SE aumentará la reutilización y el uso compartido. Por otro lado, tener una clase Pair se suma a la tentación de crear estructuras de datos complicadas a partir de pares y colecciones sin crear los tipos y abstracciones necesarios. (Esa es una paráfrasis del mensaje de Kevin Bourillion de ese hilo).
Recomiendo a todo el mundo que lea todo el hilo del correo electrónico. Es notablemente perspicaz y no tiene flamage. Es bastante convincente. Cuando comenzó, pensé: "Sí, debería haber una clase Pair en Java SE", pero cuando el hilo llegó a su fin, había cambiado de opinión.
Sin embargo, tenga en cuenta que JavaFX tiene la clase javafx.util.Pair . Las API de JavaFX evolucionaron por separado de las API de Java SE.
Como se puede ver en la pregunta vinculada ¿ Cuál es el equivalente del par C ++ en Java? hay un espacio de diseño bastante grande que rodea lo que aparentemente es una API tan simple. ¿Los objetos deben ser inmutables? ¿Deberían ser serializables? ¿Deberían ser comparables? ¿Debería ser la clase final o no? ¿Deben ordenarse los dos elementos? ¿Debería ser una interfaz o una clase? ¿Por qué detenerse en parejas? ¿Por qué no triples, cuádriceps o N-tuplas?
Y, por supuesto, está el inevitable nombramiento de bikeshed para los elementos:
- (a, b)
- (primer segundo)
- (izquierda derecha)
- (coche, cdr)
- (foo, bar)
- etc.
Un gran problema que apenas se ha mencionado es la relación de Pares con primitivos. Si tiene un dato (int x, int y)
que representa un punto en el espacio 2D, representar esto como Pair<Integer, Integer>
consume tres objetos en lugar de dos palabras de 32 bits. Además, estos objetos deben residir en el montón e incurrirán en gastos generales de GC.
Parece claro que, al igual que Streams, sería esencial que hubiera especializaciones primitivas para pares. ¿Queremos ver?
Pair
ObjIntPair
ObjLongPair
ObjDoublePair
IntObjPair
IntIntPair
IntLongPair
IntDoublePair
LongObjPair
LongIntPair
LongLongPair
LongDoublePair
DoubleObjPair
DoubleIntPair
DoubleLongPair
DoubleDoublePair
Incluso un IntIntPair
aún requeriría un objeto en el montón.
Estos son, por supuesto, una reminiscencia de la proliferación de interfaces funcionales en el paquete java.util.function
en Java SE 8. Si no quiere una API inflada, ¿cuáles dejaría de lado? También podría argumentar que esto no es suficiente, y que las especializaciones para, por ejemplo, Boolean
deberían agregarse también.
Mi sensación es que si Java hubiera agregado una clase Pair hace mucho tiempo, hubiera sido simple, o incluso simplista, y no habría satisfecho muchos de los casos de uso que estamos imaginando ahora. ¡Considere que si se hubiera agregado Pair en el marco de tiempo de JDK 1.0, probablemente habría sido mutable! (Mire java.util.Date.) ¿Habría sido feliz la gente con eso? Mi suposición es que si hubiera una clase Pair en Java, sería algo así como no muy útil y todos seguirían haciendo lo propio para satisfacer sus necesidades, habría varias implementaciones de Pair y Tuple en bibliotecas externas. y la gente todavía estaría discutiendo / discutiendo sobre cómo arreglar la clase Pair de Java. En otras palabras, algo así como en el mismo lugar en el que nos encontramos hoy.
Mientras tanto, se está trabajando para resolver el problema fundamental, que es un mejor soporte en la JVM (y eventualmente en el lenguaje Java) para los tipos de valores . Ver este documento sobre el estado de los valores . Este es un trabajo preliminar y especulativo, y cubre solo cuestiones desde la perspectiva de JVM, pero ya tiene una buena cantidad de pensamiento detrás de esto. Por supuesto, no hay garantías de que esto llegue a Java 9, o que llegue a alguna parte, pero muestra la dirección actual de pensamiento sobre este tema.
Vavr (anteriormente llamado Javaslang) ( http://www.vavr.io ) también proporciona tuplas (hasta un tamaño de 8). Aquí está el javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .
Este es un ejemplo simple:
Tuple2<Integer, String> entry = Tuple.of(1, "A");
Integer key = entry._1;
String value = entry._2;
¿Por qué JDK en sí no viene con un tipo simple de tuplas hasta ahora es un misterio para mí. Escribir clases de envoltura parece ser un asunto de todos los días.