¿Cuál es el equivalente del par C ++ <L, R> en Java?


671

¿Hay una buena razón por la cual no hay Pair<L,R>en Java? ¿Cuál sería el equivalente de esta construcción C ++? Prefiero evitar reimplementar el mío.

Parece que 1.6 está proporcionando algo similar ( AbstractMap.SimpleEntry<K,V>), pero esto parece bastante complicado.


77
¿Por qué es AbstractMap.SimpleEntrycomplicado?
CurtainDog

27
Debido al namig, nombrar arbitrariamente una clave y un valor.
Enerccio


2
@sffc JavaFX no está en ninguna de las rutas de clases predeterminadas en JDK7, su uso requiere que las bibliotecas de tiempo de ejecución JFX se agreguen manualmente.
Cord Rehn

3
@Enerccio: Entonces, ¿realmente declaras que "primero" y "segundo" no son arbitrarios, mientras que "clave" y "valor" sí lo son? Entonces esta es una buena razón para no tener esa clase en SDK. Habría una disputa eterna sobre el nombre "apropiado".
fdreger

Respuestas:


400

En un hilocomp.lang.java.help , Hunter Gratzner da algunos argumentos en contra de la presencia de una Pairconstrucción en Java. El argumento principal es que una clase Pairno transmite ninguna semántica sobre la relación entre los dos valores (¿cómo sabe lo que significa "primero" y "segundo"?).

Una mejor práctica es escribir una clase muy simple, como la que propuso Mike, para cada aplicación que hubiera hecho de la Pairclase. Map.Entryes un ejemplo de una pareja que lleva su significado en su nombre.

En resumen, en mi opinión, es mejor tener una clase Position(x,y), una clase Range(begin,end)y una clase en Entry(key,value)lugar de un genérico Pair(first,second)que no me diga nada sobre lo que se supone que debe hacer.


143
Gratzner está partiendo pelos. Estamos muy contentos de devolver un valor único como una clase primitiva o incorporada sin encapsularlo en una clase. Si tuviéramos que devolver una tupla de una docena de elementos, nadie estaría en desacuerdo, debería tener su propia clase. En algún lugar en el medio hay una línea divisoria (difusa). Creo que nuestros cerebros de lagarto pueden hacer frente a los pares con bastante facilidad.
Ian

25
Estoy de acuerdo con Ian Java te permite regresar int; no te obliga a crear un alias para int cada vez que usas uno. Los pares no son muy diferentes.
Clément

55
Si pudiéramos desempaquetar un par directamente a sus variables locales, o reenviarlo a un método que tome dos argumentos, Pair sería una clase útil. Como no podemos desempaquetarlo así, crear una clase significativa y mantener los valores juntos no se ve tan mal. Y, si realmente quieres un par a pesar de las limitaciones, siempre hay Objetos [2] + lanzamientos :-)
marcus

La cuestión es que si no está de acuerdo con Gratzner, entonces hay implementaciones de pares en varios lugares. Apache Commons y Guava tienen el IIRC. Usa esos. Pero poner algo en las principales bibliotecas de Java significa que es una forma noble y aprobada de hacer las cosas (con mayúsculas) y, dado que las personas no están de acuerdo, no debemos ponerlo allí. Hay suficiente rudeza en las viejas librerías como es, no hay que poner innecesariamente más allí.
Haakon Løtveit

1
@Dragas Cuando necesito un par de valores, entonces eso no es Java ... ¿en serio?
idclev 463035818

156

Esto es Java Debe crear su propia clase Pair personalizada con nombres descriptivos de clase y campo, y no debe preocuparse de que reinvente la rueda escribiendo hashCode () / equals () o implementando Comparable una y otra vez.


61
Eso no responde la pregunta "por qué". (A menos que se tiene en cuenta 'esto es java' una respuesta)
Nikita Rybak

127
+1 por burlarse de la verbosidad de Java. -1 por no responder realmente la pregunta.
Bennett McElwee

19
La burla de Java hubiera estado bien si hubieras señalado el Apache Commong Lang, que contiene una clase Pair.
haylem

66
O simplemente podría usarSimpleImmutableEntry
CurtainDog

33
La primera oración responde la pregunta "¿por qué?". Esto es Java, y eso es todo.
masterziv

103

Clase de par compatible con HashMap:

public class Pair<A, B> {
    private A first;
    private B second;

    public Pair(A first, B second) {
        super();
        this.first = first;
        this.second = second;
    }

    public int hashCode() {
        int hashFirst = first != null ? first.hashCode() : 0;
        int hashSecond = second != null ? second.hashCode() : 0;

        return (hashFirst + hashSecond) * hashSecond + hashFirst;
    }

    public boolean equals(Object other) {
        if (other instanceof Pair) {
            Pair otherPair = (Pair) other;
            return 
            ((  this.first == otherPair.first ||
                ( this.first != null && otherPair.first != null &&
                  this.first.equals(otherPair.first))) &&
             (  this.second == otherPair.second ||
                ( this.second != null && otherPair.second != null &&
                  this.second.equals(otherPair.second))) );
        }

        return false;
    }

    public String toString()
    { 
           return "(" + first + ", " + second + ")"; 
    }

    public A getFirst() {
        return first;
    }

    public void setFirst(A first) {
        this.first = first;
    }

    public B getSecond() {
        return second;
    }

    public void setSecond(B second) {
        this.second = second;
    }
}

136
Probablemente desee eliminar los setters, y hacer el primero y el segundo final, haciendo que el par sea inmutable. (Si alguien cambió los componentes después de usarlos como una clave hash, sucederán cosas extrañas).
Thilo

21
return "(" + first.toString () + "," + second.toString () + ")" en el método toString () puede arrojar NullPointerExceptions. Esto es mejor: return "(" + first + "," + second + ")";
Juha Syrjälä

66
Además, marque el par como "final" o cambie la primera línea de iguales a 'if (other! = Null && this.getClass () == other.getClass ())'
sargas

8
Perdón por la pregunta aleatoria de nooby, pero ¿por qué tienes una llamada a super () en el constructor?
Ibrahim

66
@Ibrahim: En este caso, es superfluo --- el comportamiento es exactamente el mismo si eliminaste super(). Normalmente lo cortaría si es opcional, como si estuviera aquí.
Chris Jester-Young

53

El par más corto que se me ocurrió es el siguiente, utilizando Lombok :

@Data
@AllArgsConstructor(staticName = "of")
public class Pair<F, S> {
    private F first;
    private S second;
}

Tiene todas las ventajas de la respuesta de @arturh (excepto la comparabilidad), tiene hashCode, equals, toStringy un “constructor” estática.


¡Hábil! ¡Le gustó!
Ahmet Ipkin


31

Otra forma de implementar Emparejar con.

  • Campos públicos inmutables, es decir, estructura de datos simple.
  • Comparable.
  • Hash simple e igual.
  • Fábrica simple para que no tenga que proporcionar los tipos. por ejemplo, Pair.of ("hola", 1);

    public class Pair<FIRST, SECOND> implements Comparable<Pair<FIRST, SECOND>> {
    
        public final FIRST first;
        public final SECOND second;
    
        private Pair(FIRST first, SECOND second) {
            this.first = first;
            this.second = second;
        }
    
        public static <FIRST, SECOND> Pair<FIRST, SECOND> of(FIRST first,
                SECOND second) {
            return new Pair<FIRST, SECOND>(first, second);
        }
    
        @Override
        public int compareTo(Pair<FIRST, SECOND> o) {
            int cmp = compare(first, o.first);
            return cmp == 0 ? compare(second, o.second) : cmp;
        }
    
        // todo move this to a helper class.
        private static int compare(Object o1, Object o2) {
            return o1 == null ? o2 == null ? 0 : -1 : o2 == null ? +1
                    : ((Comparable) o1).compareTo(o2);
        }
    
        @Override
        public int hashCode() {
            return 31 * hashcode(first) + hashcode(second);
        }
    
        // todo move this to a helper class.
        private static int hashcode(Object o) {
            return o == null ? 0 : o.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Pair))
                return false;
            if (this == obj)
                return true;
            return equal(first, ((Pair) obj).first)
                    && equal(second, ((Pair) obj).second);
        }
    
        // todo move this to a helper class.
        private boolean equal(Object o1, Object o2) {
            return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2));
        }
    
        @Override
        public String toString() {
            return "(" + first + ", " + second + ')';
        }
    }

10
Me gusta el método estático de fábrica of. Recuerda las colecciones inmutables de Google Guava .
Jarek Przygódzki

77
Usted está en algún punto de fundición o1a Comparable, aunque nada indica que en realidad va a poner en práctica esa interfaz. Si ese es un requisito, el FIRSTparámetro tipo debería serlo FIRST extends Comparable<?>.
G_H

No soy un chico de Java, así que perdóname por mi ignorancia, pero ¿en qué clase de ayudante estabas pensando en los comentarios de TODO?

3
31 es una constante constante para hashCode. Por ejemplo, si usa HashMap tecleado por Pair <Integer, Integer> para el mapa 2D, obtendrá muchas colisiones. Por ejemplo (a * 65497) ^ b sería más adecuado.
Michał Zieliński

1
@MarioCarneiro ^ es xor, no poder
Michał Zieliński

27

¿Qué tal http://www.javatuples.org/index.html? Lo he encontrado muy útil.

El javatuples le ofrece clases de tuplas de uno a diez elementos:

Unit<A> (1 element)
Pair<A,B> (2 elements)
Triplet<A,B,C> (3 elements)
Quartet<A,B,C,D> (4 elements)
Quintet<A,B,C,D,E> (5 elements)
Sextet<A,B,C,D,E,F> (6 elements)
Septet<A,B,C,D,E,F,G> (7 elements)
Octet<A,B,C,D,E,F,G,H> (8 elements)
Ennead<A,B,C,D,E,F,G,H,I> (9 elements)
Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)

66
Es curioso, pero hay al menos 5 clases más de las que podría imaginar usar.
maaartinus

3
@maaartinus Al menos 10 más de lo que usaría.
Boann

77
@Boann: OK, me quedo corregido. Solía ​​usar Pairy podría imaginar usar Triplettal vez una vez cada 50 años. Ahora uso Lombok y creo una pequeña clase de 4 líneas cada vez que necesito un par. Entonces "10 demasiado" es exacto.
maaartinus

55
¿Necesitamos una Bottom (0 element)clase? :)
Earth Engine

2
Wow esto es feo. Sé que están tratando de hacerlo explícito, pero una Tupla con parámetros sobrecargados como en C # hubiera sido mejor.
arviman

12

Depende de para qué lo quieras usar. La razón típica para hacerlo es iterar sobre los mapas, para lo cual simplemente debe hacer esto (Java 5+):

Map<String, Object> map = ... ; // just an example
for (Map.Entry<String, Object> entry : map.entrySet()) {
  System.out.printf("%s -> %s\n", entry.getKey(), entry.getValue());
}

1
No estoy seguro de que una clase personalizada ayudaría en este caso :)
Nikita Rybak

31
"La razón típica para hacerlo es iterar sobre los mapas". De Verdad?
Bennett McElwee

12

android proporciona la Pairclase ( http://developer.android.com/reference/android/util/Pair.html ), aquí la implementación:

public class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Pair)) {
            return false;
        }
        Pair<?, ?> p = (Pair<?, ?>) o;
        return Objects.equal(p.first, first) && Objects.equal(p.second, second);
    }

    @Override
    public int hashCode() {
        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
    }

    public static <A, B> Pair <A, B> create(A a, B b) {
        return new Pair<A, B>(a, b);
    }
}

1
Objects.equal(..)requiere la biblioteca de guayaba.
Markus L

3
Cámbielo a lo Objects.equals(...)que ha estado en Java desde 2011 (1.7).
AndrewF

9

El mayor problema es probablemente que uno no puede garantizar la inmutabilidad en A y B (consulte Cómo asegurarse de que los parámetros de tipo sean inmutables ), por lo que hashCode()puede dar resultados inconsistentes para el mismo par después de que se inserte en una colección, por ejemplo (esto daría un comportamiento indefinido , vea Definir iguales en términos de campos mutables ). Para una clase de par particular (no genérica), el programador puede garantizar la inmutabilidad eligiendo cuidadosamente A y B para que sean inmutables.

De todos modos, borrando las advertencias genéricas de la respuesta de @ PeterLawrey (java 1.7):

public class Pair<A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
        implements Comparable<Pair<A, B>> {

    public final A first;
    public final B second;

    private Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }

    public static <A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
            Pair<A, B> of(A first, B second) {
        return new Pair<A, B>(first, second);
    }

    @Override
    public int compareTo(Pair<A, B> o) {
        int cmp = o == null ? 1 : (this.first).compareTo(o.first);
        return cmp == 0 ? (this.second).compareTo(o.second) : cmp;
    }

    @Override
    public int hashCode() {
        return 31 * hashcode(first) + hashcode(second);
    }

    // TODO : move this to a helper class.
    private static int hashcode(Object o) {
        return o == null ? 0 : o.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair))
            return false;
        if (this == obj)
            return true;
        return equal(first, ((Pair<?, ?>) obj).first)
                && equal(second, ((Pair<?, ?>) obj).second);
    }

    // TODO : move this to a helper class.
    private boolean equal(Object o1, Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ')';
    }
}

Adiciones / correcciones muy bienvenidas :) En particular, no estoy muy seguro acerca de mi uso de Pair<?, ?>.

Para obtener más información sobre por qué esta sintaxis, consulte Asegúrese de que los objetos implementen Comparable y para una explicación detallada ¿Cómo implementar una max(Comparable a, Comparable b)función genérica en Java?


Como los enteros Java son de 32 bits, ¿multiplicar el primer código hash por 31 no significa que se desborde? ¿No sería mejor realizar un quirófano exclusivo?
Dan

@Dan, siéntase libre de editar Me mudé de Java :)
Mr_and_Mrs_D

5

En mi opinión, no hay un par en Java porque, si desea agregar funcionalidad adicional directamente en el par (por ejemplo, comparable), debe vincular los tipos. En C ++, simplemente no nos importa, y si los tipos que componen un par no tienen operator <, el pair::operator <no se compilará también.

Un ejemplo de Comparable sin límite:

public class Pair<F, S> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static int compare(Object l, Object r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : ((Comparable) (l)).compareTo(r);
        }
    }
}

/* ... */

Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
//Runtime error here instead of compile error!
System.out.println(a.compareTo(b));

Un ejemplo de Comparable con la verificación en tiempo de compilación para determinar si los argumentos de tipo son comparables:

public class Pair<
        F extends Comparable<? super F>, 
        S extends Comparable<? super S>
> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static <
            T extends Comparable<? super T>
    > int compare(T l, T r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : l.compareTo(r);
        }
    }
}

/* ... */

//Will not compile because Thread is not Comparable<? super Thread>
Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
System.out.println(a.compareTo(b));

Esto es bueno, pero esta vez no puede usar tipos no comparables como argumentos de tipo en Par. Uno puede usar muchos comparadores para pares en alguna clase de utilidad, pero las personas de C ++ pueden no obtenerlo. Otra forma es escribir muchas clases en una jerarquía de tipos con diferentes límites en los argumentos de tipo, pero hay demasiados límites posibles y sus combinaciones ...


5

JavaFX (que viene incluido con Java 8) tiene la clase Pair <A, B>


1
La implementación de hashCode en javafx.util.Pair puede provocar colisiones en casos triviales. Usarlo en HashMap / HashTable seguirá funcionando ya que Java verifica la igualdad de valores además de los códigos hash, pero es algo a tener en cuenta.
sffc

Esa es una implementación de hashCode muy estándar y comúnmente recomendada. Se deben esperar colisiones por cualquier código que llame hashCode(). Tenga en cuenta que Java en sí no llama a este método. Es para el código de usuario, incluidas las bibliotecas.
AndrewF

5

Como muchos otros ya han dicho, realmente depende del caso de uso si una clase Pair es útil o no.

Creo que para una función auxiliar privada es totalmente legítimo usar una clase Pair si eso hace que su código sea más legible y no vale la pena el esfuerzo de crear otra clase de valor con todo su código de placa de caldera.

Por otro lado, si su nivel de abstracción requiere que documente claramente la semántica de la clase que contiene dos objetos o valores, entonces debe escribir una clase para ello. Por lo general, ese es el caso si los datos son un objeto comercial.

Como siempre, requiere un juicio experto.

Para su segunda pregunta, recomiendo la clase Pair de las bibliotecas de Apache Commons. Esos podrían considerarse como bibliotecas estándar extendidas para Java:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html

También es posible que desee echar un vistazo a EqualsBuilder , HashCodeBuilder y ToStringBuilder de Apache Commons , que simplifican la escritura de clases de valor para sus objetos comerciales.


La URL actualizada es commons.apache.org/lang/api-release/index.html?org/apache/… ya que commons-lang3 está fuera de beta. Esto es incluso más corto que mi propia solución de Lombok si ya usa commons-lang 3.
Michael Piefel


5

Buenas noticias JavaFXtiene un valor clave Par.

simplemente agregue javafx como dependencia e importe javafx.util.Pair;

y usar simplemente como en c++.

Pair <Key, Value> 

p.ej

Pair <Integer, Integer> pr = new Pair<Integer, Integer>()

pr.get(key);// will return corresponding value

La mala noticia es que no todos usan JavaFX
Michał Dobi Dobrzański



3

De acuerdo con la naturaleza del lenguaje Java, supongo que las personas no requieren realmente Pairuna interfaz, generalmente es lo que necesitan. Aquí hay un ejemplo:

interface Pair<L, R> {
    public L getL();
    public R getR();
}

Entonces, cuando las personas desean devolver dos valores, pueden hacer lo siguiente:

... //Calcuate the return value
final Integer v1 = result1;
final String v2 = result2;
return new Pair<Integer, String>(){
    Integer getL(){ return v1; }
    String getR(){ return v2; }
}

Esta es una solución bastante ligera y responde a la pregunta "¿Cuál es la semántica de un Pair<L,R>?". La respuesta es, esta es una compilación de interfaz con dos (puede ser diferente) tipos, y tiene métodos para devolver cada uno de ellos. Depende de usted agregarle más semántica. Por ejemplo, si está utilizando Posición y REALMENTE desea indicarlo en su código, puede definir PositionXy PositionYque contiene Integer, para componer a Pair<PositionX,PositionY>. Si JSR 308 está disponible, también puede usarlo Pair<@PositionX Integer, @PositionY Ingeger>para simplificar eso.

EDITAR: Una cosa que debo indicar aquí es que la definición anterior relaciona explícitamente el nombre del parámetro de tipo y el nombre del método. Esta es una respuesta a los argumentos de que a Paires falta de información semántica. En realidad, el método getLsignifica "dame el elemento que corresponde al tipo de parámetro tipo L", que significa algo.

EDITAR: Aquí hay una clase de utilidad simple que puede hacer la vida más fácil:

class Pairs {
    static <L,R> Pair<L,R> makePair(final L l, final R r){
        return new Pair<L,R>(){
            public L getL() { return l; }
            public R getR() { return r; }   
        };
    }
}

uso:

return Pairs.makePair(new Integer(100), "123");

¿Qué pasa con equals, hashCodey toString?
sdgfsdh

bueno, esta es solo una implementación mínima. Si necesita más que eso, puede escribir algunas funciones auxiliares para que sea más fácil, pero aún necesita escribir el código.
Earth Engine

Para implementar toStringnecesita más conocimiento sobre la relación entre los dos campos.
Earth Engine

Lo que quiero decir es que classpodría ser mejor que solo interfaceporque puede implementar estas cosas.
sdgfsdh 01 de

3

A pesar de ser sintácticamente similares, Java y C ++ tienen paradigmas muy diferentes. Escribir C ++ como Java es malo C ++, y escribir Java como C ++ es malo Java.

Con un IDE basado en la reflexión como Eclipse, escribir la funcionalidad necesaria de una clase de "par" es rápido y simple. Cree una clase, defina dos campos, use las diversas opciones de menú "Generar XX" para completar la clase en cuestión de segundos. Tal vez tendrías que escribir un "compareTo" muy rápido si quisieras la interfaz Comparable.

Con opciones separadas de declaración / definición en el lenguaje, los generadores de código C ++ no son tan buenos, por lo que escribir a mano pequeñas clases de utilidad es más tedioso. Debido a que el par es una plantilla, no tiene que pagar por funciones que no usa, y la función typedef permite asignar nombres de tipo significativos al código, por lo que las objeciones sobre "sin semántica" realmente no se sostienen.


2

El par sería una buena cosa, para ser una unidad de construcción básica para un genérico complejo, por ejemplo, esto es de mi código:

WeakHashMap<Pair<String, String>, String> map = ...

Es lo mismo que la Tupla de Haskell


1
Ahora puedo decir que usar Pair <A, B> hace que el código sea menos informativo, e implementar objetos especiales en lugar de usar Pair es mucho mejor
Illarion Kovalchuk

1
Mejor o peor. Imagine que tiene una función que combina sus dos argumentos (por ejemplo, fusionar gráficos en uno) y necesita almacenarla en caché. Aquí, Paires óptimo ya que no hay una semántica especial. Tener un nombre claro para un concepto claro es bueno, pero buscar un nombre donde "primero" y "segundo" funcionan bien no lo es.
maaartinus

2

Manera simple Object [] - puede usarse como cualquier tupla de dimensión


2
Cualquier dimensión, sí. Pero: engorroso de crear, y no es seguro para escribir.
Michael Piefel

2

Para lenguajes de programación como Java, la estructura de datos alternativa utilizada por la mayoría de los programadores para representar pares como estructuras de datos son dos matrices, y se accede a los datos a través del mismo índice

ejemplo: http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080

Esto no es ideal ya que los datos deben estar unidos, pero también resultan ser bastante baratos. Además, si su caso de uso exige el almacenamiento de coordenadas, es mejor construir su propia estructura de datos.

Tengo algo como esto en mi biblioteca

public class Pair<First,Second>{.. }


2

Aquí hay algunas bibliotecas que tienen múltiples grados de tuplas para su conveniencia:

  • JavaTuples . Tuplas de grado 1-10 es todo lo que tiene.
  • JavaSlang . Tuplas de grado 0-8 y muchas otras cosas funcionales.
  • jOOλ . Tuplas de grado 0-16 y algunas otras cosas funcionales. (Descargo de responsabilidad, trabajo para la empresa mantenedora)
  • Java funcional . Tuplas de grado 0-8 y muchas otras cosas funcionales.

Se ha mencionado que otras bibliotecas contienen al menos el Pair tupla.

Específicamente, en el contexto de la programación funcional que utiliza una gran cantidad de tipeo estructural, en lugar de tipeo nominal ( como se recomienda en la respuesta aceptada ), esas bibliotecas y sus tuplas son muy útiles.



2

otra implementación concisa de lombok

import lombok.Value;

@Value(staticConstructor = "of")
public class Pair<F, S> {
    private final F first;
    private final S second;
}

1

Noté que todas las implementaciones de Par esparcidas por aquí atribuyen significado al orden de los dos valores. Cuando pienso en un par, pienso en una combinación de dos elementos en los que el orden de los dos no tiene importancia. Aquí está mi implementación de un par desordenado, con hashCodey equalsreemplaza para garantizar el comportamiento deseado en las colecciones. También se puede clonar.

/**
 * The class <code>Pair</code> models a container for two objects wherein the
 * object order is of no consequence for equality and hashing. An example of
 * using Pair would be as the return type for a method that needs to return two
 * related objects. Another good use is as entries in a Set or keys in a Map
 * when only the unordered combination of two objects is of interest.<p>
 * The term "object" as being a one of a Pair can be loosely interpreted. A
 * Pair may have one or two <code>null</code> entries as values. Both values
 * may also be the same object.<p>
 * Mind that the order of the type parameters T and U is of no importance. A
 * Pair&lt;T, U> can still return <code>true</code> for method <code>equals</code>
 * called with a Pair&lt;U, T> argument.<p>
 * Instances of this class are immutable, but the provided values might not be.
 * This means the consistency of equality checks and the hash code is only as
 * strong as that of the value types.<p>
 */
public class Pair<T, U> implements Cloneable {

    /**
     * One of the two values, for the declared type T.
     */
    private final T object1;
    /**
     * One of the two values, for the declared type U.
     */
    private final U object2;
    private final boolean object1Null;
    private final boolean object2Null;
    private final boolean dualNull;

    /**
     * Constructs a new <code>Pair&lt;T, U&gt;</code> with T object1 and U object2 as
     * its values. The order of the arguments is of no consequence. One or both of
     * the values may be <code>null</code> and both values may be the same object.
     *
     * @param object1 T to serve as one value.
     * @param object2 U to serve as the other value.
     */
    public Pair(T object1, U object2) {

        this.object1 = object1;
        this.object2 = object2;
        object1Null = object1 == null;
        object2Null = object2 == null;
        dualNull = object1Null && object2Null;

    }

    /**
     * Gets the value of this Pair provided as the first argument in the constructor.
     *
     * @return a value of this Pair.
     */
    public T getObject1() {

        return object1;

    }

    /**
     * Gets the value of this Pair provided as the second argument in the constructor.
     *
     * @return a value of this Pair.
     */
    public U getObject2() {

        return object2;

    }

    /**
     * Returns a shallow copy of this Pair. The returned Pair is a new instance
     * created with the same values as this Pair. The values themselves are not
     * cloned.
     *
     * @return a clone of this Pair.
     */
    @Override
    public Pair<T, U> clone() {

        return new Pair<T, U>(object1, object2);

    }

    /**
     * Indicates whether some other object is "equal" to this one.
     * This Pair is considered equal to the object if and only if
     * <ul>
     * <li>the Object argument is not null,
     * <li>the Object argument has a runtime type Pair or a subclass,
     * </ul>
     * AND
     * <ul>
     * <li>the Object argument refers to this pair
     * <li>OR this pair's values are both null and the other pair's values are both null
     * <li>OR this pair has one null value and the other pair has one null value and
     * the remaining non-null values of both pairs are equal
     * <li>OR both pairs have no null values and have value tuples &lt;v1, v2> of
     * this pair and &lt;o1, o2> of the other pair so that at least one of the
     * following statements is true:
     * <ul>
     * <li>v1 equals o1 and v2 equals o2
     * <li>v1 equals o2 and v2 equals o1
     * </ul>
     * </ul>
     * In any other case (such as when this pair has two null parts but the other
     * only one) this method returns false.<p>
     * The type parameters that were used for the other pair are of no importance.
     * A Pair&lt;T, U> can return <code>true</code> for equality testing with
     * a Pair&lt;T, V> even if V is neither a super- nor subtype of U, should
     * the the value equality checks be positive or the U and V type values
     * are both <code>null</code>. Type erasure for parameter types at compile
     * time means that type checks are delegated to calls of the <code>equals</code>
     * methods on the values themselves.
     *
     * @param obj the reference object with which to compare.
     * @return true if the object is a Pair equal to this one.
     */
    @Override
    public boolean equals(Object obj) {

        if(obj == null)
            return false;

        if(this == obj)
            return true;

        if(!(obj instanceof Pair<?, ?>))
            return false;

        final Pair<?, ?> otherPair = (Pair<?, ?>)obj;

        if(dualNull)
            return otherPair.dualNull;

        //After this we're sure at least one part in this is not null

        if(otherPair.dualNull)
            return false;

        //After this we're sure at least one part in obj is not null

        if(object1Null) {
            if(otherPair.object1Null) //Yes: this and other both have non-null part2
                return object2.equals(otherPair.object2);
            else if(otherPair.object2Null) //Yes: this has non-null part2, other has non-null part1
                return object2.equals(otherPair.object1);
            else //Remaining case: other has no non-null parts
                return false;
        } else if(object2Null) {
            if(otherPair.object2Null) //Yes: this and other both have non-null part1
                return object1.equals(otherPair.object1);
            else if(otherPair.object1Null) //Yes: this has non-null part1, other has non-null part2
                return object1.equals(otherPair.object2);
            else //Remaining case: other has no non-null parts
                return false;
        } else {
            //Transitive and symmetric requirements of equals will make sure
            //checking the following cases are sufficient
            if(object1.equals(otherPair.object1))
                return object2.equals(otherPair.object2);
            else if(object1.equals(otherPair.object2))
                return object2.equals(otherPair.object1);
            else
                return false;
        }

    }

    /**
     * Returns a hash code value for the pair. This is calculated as the sum
     * of the hash codes for the two values, wherein a value that is <code>null</code>
     * contributes 0 to the sum. This implementation adheres to the contract for
     * <code>hashCode()</code> as specified for <code>Object()</code>. The returned
     * value hash code consistently remain the same for multiple invocations
     * during an execution of a Java application, unless at least one of the pair
     * values has its hash code changed. That would imply information used for 
     * equals in the changed value(s) has also changed, which would carry that
     * change onto this class' <code>equals</code> implementation.
     *
     * @return a hash code for this Pair.
     */
    @Override
    public int hashCode() {

        int hashCode = object1Null ? 0 : object1.hashCode();
        hashCode += (object2Null ? 0 : object2.hashCode());
        return hashCode;

    }

}

Esta implementación se ha probado correctamente en la unidad y se ha probado su uso en un conjunto y un mapa.

Tenga en cuenta que no pretendo publicar esto en el dominio público. Este es un código que acabo de escribir para usarlo en una aplicación, por lo que si va a usarlo, evite hacer una copia directa y juegue un poco con los comentarios y los nombres. ¿Me entiendes?


3
en realidad, verifique la parte inferior de cada página: "contribuciones de usuarios con licencia bajo cc-wiki"
amara

Ah, no me había dado cuenta de eso. Gracias por el aviso. En ese caso, use el código como mejor le parezca a esa licencia.
G_H

1
La pregunta es sobre un par equivalente de C ++, que está ordenado. También creo que siempre y cuando uno tenga una referencia al objeto Pair y esos sean mutables, insertar pares en colecciones puede conducir a un comportamiento indefinido.
Mr_and_Mrs_D


1

com.sun.tools.javac.util.Pair es una implementación simple de un par. Se puede encontrar en jdk1.7.0_51 \ lib \ tools.jar.

Aparte de org.apache.commons.lang3.tuple.Pair, no es solo una interfaz.


2
Sin embargo, nadie debería usar las API internas de JDK.
jpangamarca

0
public class Pair<K, V> {

    private final K element0;
    private final V element1;

    public static <K, V> Pair<K, V> createPair(K key, V value) {
        return new Pair<K, V>(key, value);
    }

    public Pair(K element0, V element1) {
        this.element0 = element0;
        this.element1 = element1;
    }

    public K getElement0() {
        return element0;
    }

    public V getElement1() {
        return element1;
    }

}

uso:

Pair<Integer, String> pair = Pair.createPair(1, "test");
pair.getElement0();
pair.getElement1();

¡Inmutable, solo un par!


Oh wow. ¿Otro? Intente usar el suyo con genéricos más complejos: en algún momento, no podrá inferir los tipos apropiados. Además, lo siguiente debería ser posible: ¿ Pair<Object, Object> pair = Pair.createPair("abc", "def")pero creo que uno necesita escribir Pair.createPair((Object)"abc", (Object)"def")con su código?
HA SALIDO - Anony-Mousse

puede reemplazar el método estático por esto: @SuppressWarnings("unchecked") public static <K, V, X, Y> Pair<X, Y> createPair(K key, V value) { return new Pair<X, Y>((X) key, (Y) value); } pero no sé si es una buena práctica
Bastiflew

No, eso probablemente solo arruinará las cosas aún más. En mi experiencia, al menos uno de los compiladores (prueba java6, java7, javadoc y eclipse java compiler) se quejará. Lo tradicional new Pair<Object, Object>("abc", "def")fue lo más confiable en mis experimentos.
HA SALIDO - Anony-Mousse
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.