Qual é o equivalente do par C ++ <L, R> em Java?


671

Existe uma boa razão para não existir Pair<L,R>em Java? Qual seria o equivalente dessa construção C ++? Prefiro evitar reimplementar o meu.

Parece que o 1.6 está fornecendo algo semelhante ( AbstractMap.SimpleEntry<K,V>), mas isso parece bastante complicado.


7
Por que é AbstractMap.SimpleEntrycomplicado?
CurtainDog

27
Por causa do namig, nomear arbitrariamente uma chave e um valor.
Enerccio 15/09/14


2
@sffc O JavaFX não está em nenhum dos caminhos de classe padrão no JDK7, pois é necessário que as bibliotecas de tempo de execução JFX sejam adicionadas manualmente.
Cord Rehn

3
@Enerccio: Então, você realmente afirma que "primeiro" e "segundo" não são arbitrários, enquanto "chave" e "valor" - é? Esse é um bom motivo para não ter essa classe no SDK. Haveria uma disputa eterna sobre a nomeação "adequada".
fdreger 11/09/16

Respostas:


400

Em um tópicocomp.lang.java.help , Hunter Gratzner apresenta alguns argumentos contra a presença de uma Pairconstrução em Java. O argumento principal é que uma classe Pairnão transmite nenhuma semântica sobre o relacionamento entre os dois valores (como você sabe o que "primeiro" e "segundo" significam?).

Uma prática melhor é escrever uma classe muito simples, como a proposta por Mike, para cada aplicativo que você faria da Pairclasse. Map.Entryé um exemplo de um par que carrega seu significado em seu nome.

Para resumir, na minha opinião, é melhor ter uma classe Position(x,y), uma classe Range(begin,end)e uma classe Entry(key,value)do que um genérico Pair(first,second)que não me diga nada sobre o que deve fazer.


143
Gratzner está cortando cabelos. Estamos felizes em retornar um único valor como uma classe primitiva ou interna sem encapsulá-lo em uma classe. Se devêssemos retornar uma tupla de uma dúzia de elementos, ninguém discordaria que deveria ter sua própria classe. Em algum lugar no meio há uma linha divisória (confusa). Acho que nossos cérebros de lagartos conseguem lidar com pares com bastante facilidade.
31416 Ian

25
Eu concordo com o Ian. Java permite retornar int; não força você a criar um alias para int toda vez que você usa um. Pares não são muito diferentes.
Clément

5
Se pudéssemos descompactar um par diretamente para suas variáveis ​​locais ou encaminhá-lo para um método que usa dois argumentos, Pair seria uma classe útil. Como não podemos desempacotá-lo dessa maneira, criar uma classe significativa e manter os valores juntos não parece muito ruim. E, se você realmente quer um par apesar das limitações, há sempre Object [2] + moldes :-)
marcus

O fato é que, se você não concorda com Gratzner, há implementações de pares em vários lugares. O Apache Commons e o Guava possuem o IIRC. Use aqueles. Mas colocar algo nas principais bibliotecas Java significa que é uma maneira nobre e aprovada de fazer as coisas (com letras maiúsculas) e, como as pessoas não concordam com isso, não devemos colocá-lo lá. Há fragmentos suficientes nas bibliotecas antigas, como é, não vamos desnecessariamente colocar mais lá.
Haakon Løtveit 11/08/19

1
@Dragas Quando eu preciso de um par de valores, então isso não é Java ... sério?
idclev 463035818

156

Isto é Java. Você precisa criar sua própria classe Pair personalizada, com nomes descritivos de classe e campo, e não se esqueça de reinventar a roda escrevendo hashCode () / equals () ou implementando Comparable repetidamente.


61
Isso não responde à pergunta "por que". (A menos que você considere 'isto é java' uma resposta)
Nikita Rybak

127
+1 para zombar da verbosidade do Java. -1 por não responder à pergunta.
Bennett McElwee

19
A zombaria de Java ficaria bem se você tivesse apontado para o Apache Commong Lang, que contém uma classe Pair.
21412 haylem

6
Ou você pode simplesmente usarSimpleImmutableEntry
CurtainDog

33
A primeira frase responde à pergunta "por quê?". Isso é Java, e é isso.
masterziv

103

Classe de par compatível com 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
Você provavelmente deseja excluir os levantadores e fazer a primeira e a segunda finais, tornando o par imutável. (Se alguém alterar os componentes depois de usá-los como chave de hash, coisas estranhas acontecerão).
Thilo

21
retornar "(" + first.toString () + "," + second.toString () + ")" no método toString () pode gerar NullPointerExceptions. Isso é melhor: return "(" + primeiro + "," + segundo + ")";
Juha Syrjälä 01/09/09

6
Além disso, marque o par como "final" ou altere a primeira linha de iguais para 'if (other! = Null && this.getClass () == other.getClass ())'
Sargas

8
Desculpe pela pergunta aleatória nooby, mas por que você chama super () no construtor?
Ibrahim

6
@Ibrahim: Neste caso, é supérfluo --- o comportamento é exatamente o mesmo se você tirou a super()bola. Normalmente, eu apenas cortaria se fosse opcional, como se estivesse aqui.
Chris Jester-Young

53

O par mais curto que eu pude criar é o seguinte, usando Lombok :

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

Possui todos os benefícios da resposta de @arturh (exceto a comparabilidade), possui hashCode,equals , toStringe um “construtor” estática.


Bacana! Gostei!
Ahmet Ipkin


31

Outra maneira de implementar o Pair with.

  • Campos públicos imutáveis, ou seja, estrutura de dados simples.
  • Comparável.
  • Hash simples e iguais.
  • Fábrica simples, para que você não precise fornecer os tipos. por exemplo Pair.of ("olá", 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
Eu gosto do método estático de fábrica of. Ele lembra coleções imutáveis do Google Guava .
Jarek Przygódzki

7
Você está em algum ponto lançando o1para Comparable, mesmo que nada indica que ele vai realmente implementar essa interface. Se isso é um requisito, o FIRSTparâmetro type deve ser FIRST extends Comparable<?>.
G_H

Eu não sou um cara de Java, então, por favor, perdoe-me por minha ignorância, mas que tipo de aulas de auxiliar você estava pensando nos comentários do TODO?

3
31 é uma constante ruim para o hashCode. Por exemplo, se você usar o HashMap codificado por Pair <Inteiro, Inteiro> para o mapa 2D, obterá muitas colisões. Por exemplo (a * 65497) ^ b seria mais adequado.
Michał Zieliński

1
@MarioCarneiro ^ é XOR, não poder
Michał Zieliński

27

Que tal http://www.javatuples.org/index.html eu achei muito útil.

Os javatuples oferecem classes de tupla de um a dez 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)

6
Engraçado, mas há pelo menos mais 5 aulas do que eu poderia imaginar usar.
Maaartinus 3/11

3
@maaartinus Pelo menos 10 a mais do que eu usaria.
Boann

7
@ Boann: OK, eu fico corrigido. Eu costumava usar Paire poderia imaginar usar Triplettalvez uma vez a cada 50 anos. Agora eu uso Lombok e crio uma pequena classe de 4 linhas toda vez que preciso de um par. Então "10 em excesso" é exato.
Maaartinus 30/01

5
Precisamos de uma Bottom (0 element)aula? :)
Earth Engine

2
Uau, isso é feio. Eu sei que eles estão tentando torná-lo explícito, mas uma Tupla com parâmetros sobrecarregados como em C # teria sido melhor.
arviman

12

Depende do que você deseja usá-lo. O motivo típico para fazer isso é iterar nos mapas, para os quais você simplesmente faz isso (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
Eu não tenho certeza uma classe personalizada ajudaria neste caso :)
Nikita Rybak

31
"O motivo típico para fazer isso é iterar nos mapas". Mesmo?
Bennett McElwee

12

android fornece Pairclasse ( http://developer.android.com/reference/android/util/Pair.html ), aqui a implementação:

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(..)requer a biblioteca Guava.
Markus L

3
Altere para o Objects.equals(...)que está em Java desde 2011 (1.7).
AndrewF

9

O maior problema é provavelmente que não se pode garantir a imutabilidade em A e B (consulte Como garantir que os parâmetros de tipo sejam imutáveis ), portanto, hashCode()pode fornecer resultados inconsistentes para o mesmo par após a inserção em uma coleção, por exemplo (isso daria um comportamento indefinido , consulte Definindo iguais em termos de campos mutáveis ). Para uma classe de par específica (não genérica), o programador pode garantir a imutabilidade escolhendo cuidadosamente A e B para serem imutáveis.

Enfim, limpando os avisos de genéricos da resposta 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 + ')';
    }
}

Adições / correções muito bem-vindas :) Em particular, não tenho muita certeza sobre meu uso de Pair<?, ?> .

Para obter mais informações sobre por que essa sintaxe, consulte Garantir que os objetos implementem Comparável e para obter uma explicação detalhada Como implementar uma max(Comparable a, Comparable b)função genérica em Java?


Como os números inteiros Java são de 32 bits, multiplicar o primeiro código hash por 31 significa que ele excede o limite? Não seria melhor realizar uma sala de cirurgia exclusiva?
Dan

@ Dan sinta-se livre para editar me mudei de java :)
Mr_and_Mrs_D 15/03

5

Na minha opinião, não há Par em Java porque, se você deseja adicionar funcionalidade extra diretamente ao par (por exemplo, Comparável), deve vincular os tipos. Em C ++, simplesmente não nos importamos e, se os tipos que compõem um par não tiverem operator <, opair::operator < eles também não serão compilados.

Um exemplo de Comparável sem limite:

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));

Um exemplo de Comparável com verificação em tempo de compilação para saber se os argumentos de tipo são comparáveis:

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));

Isso é bom, mas desta vez você não pode usar tipos não comparáveis ​​como argumentos de tipo no par. Pode-se usar muitos Comparadores para Par em alguma classe de utilitário, mas as pessoas em C ++ podem não entender. Outra maneira é escrever muitas classes em uma hierarquia de tipos com limites diferentes nos argumentos de tipo, mas existem muitos limites possíveis e suas combinações ...


5

O JavaFX (que acompanha o Java 8) possui a classe Pair <A, B>


1
A implementação do hashCode no javafx.util.Pair pode levar a colisões em casos triviais. Usá-lo no HashMap / HashTable ainda funcionará, já que o Java verifica a igualdade de valores, além dos códigos de hash, mas é importante estar ciente disso.
Sffc 23/10/2015

Essa é uma implementação hashCode muito padrão e comumente recomendada. Colisões devem ser esperadas por qualquer código que chamar hashCode(). Observe que o próprio Java não chama esse método. É para código de usuário, incluindo bibliotecas.
AndrewF

5

Como muitos outros já declararam, realmente depende do caso de uso se uma classe Pair é útil ou não.

Eu acho que, para uma função auxiliar privada, é totalmente legítimo usar uma classe Pair se isso torna seu código mais legível e não vale a pena o esforço de criar mais uma classe de valor com todo o código da placa da caldeira.

Por outro lado, se o seu nível de abstração exigir que você documente claramente a semântica da classe que contém dois objetos ou valores, você deve escrever uma classe para ele. Normalmente, esse é o caso se os dados forem um objeto de negócios.

Como sempre, requer julgamento qualificado.

Para sua segunda pergunta, recomendo a classe Pair das bibliotecas Apache Commons. Essas podem ser consideradas bibliotecas padrão estendidas para Java:

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

Você também pode dar uma olhada no EqualsBuilder , no HashCodeBuilder e no ToStringBuilder do Apache Commons , que simplificam a gravação de classes de valor para seus objetos de negócios.


O URL atualizado é commons.apache.org/lang/api-release/index.html?org/apache/…, pois commons-lang3 está fora da versão beta. Isto é ainda mais curto do que a minha própria solução Lombok, se você já usa commons-lang 3.
Michael Piefel


5

Boas notícias JavaFX tem um valor-chave Par.

basta adicionar javafx como uma dependência e importar javafx.util.Pair ;

e use simplesmente como em c++.

Pair <Key, Value> 

por exemplo

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

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

A má notícia é que nem todo mundo usa JavaFX
Michał Dobi Dobrzański

4

A interface Map.Entry chega bem perto do par de c ++. Observe a implementação concreta, como AbstractMap.SimpleEntry e AbstractMap.SimpleImmutableEntry O primeiro item é getKey () e o segundo é getValue ().


1
O OP já está ciente dessa opção e foi discutido detalhadamente.
Keegan


3

De acordo com a natureza da linguagem Java, suponho que as pessoas não precisem realmente de Pairuma interface, geralmente é o que precisam. Aqui está um exemplo:

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

Portanto, quando as pessoas desejam retornar dois valores, podem fazer o seguinte:

... //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; }
}

Essa é uma solução bastante leve e responde à pergunta "Qual é a semântica de a Pair<L,R>?". A resposta é: esta é uma interface criada com dois tipos (podem ser diferentes) e possui métodos para retornar cada um deles. Cabe a você adicionar mais semântica a ele. Por exemplo, se você estiver usando Position e REALMENTE quiser indicá-lo em seu código, poderá definir PositionXe PositionYque contém Integer, para compor a Pair<PositionX,PositionY>. Se o JSR 308 estiver disponível, você também poderá usá-lo Pair<@PositionX Integer, @PositionY Ingeger>para simplificar isso.

EDIT: Uma coisa que devo indicar aqui é que a definição acima relaciona explicitamente o nome do parâmetro type e o nome do método. Esta é uma resposta para aqueles que argumentam que a Pairé falta de informação semântica. Na verdade, o método getLsignifica "me dê o elemento que corresponde ao tipo de parâmetro do tipo L", o que significa alguma coisa.

EDIT: Aqui está uma classe de utilitário simples que pode facilitar a vida:

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");

Que tal equals, hashCodee toString?
Sdgfsdh

bem, isso é apenas uma implementação mínima. Se você precisar de mais do que isso, poderá escrever algumas funções auxiliares para facilitar, mas ainda assim precisará escrever o código.
Earth Engine

Para implementar, toStringvocê precisa de mais conhecimento sobre o relacionamento entre os dois campos.
Earth Engine

Meu ponto é fornecer um que classpode ser melhor do que apenas um, interfaceporque ele pode implementar essas coisas.
Sdgfsdh

3

Apesar de sintaticamente semelhantes, Java e C ++ têm paradigmas muito diferentes. Escrever C ++ como Java é ruim em C ++ e escrever Java como C ++ é ruim em Java.

Com um IDE baseado em reflexão como o Eclipse, escrever a necessariamente funcionalidade de uma classe "pair" é rápido e simples. Crie uma classe, defina dois campos, use as várias opções de menu "Gerar XX" para preencher a classe em questão de segundos. Talvez você precise digitar um "compareTo" rapidamente, se quiser a interface comparável.

Com opções separadas de declaração / definição na linguagem, os geradores de código C ++ não são tão bons, então escrever manualmente pequenas classes de utilitário consome mais tempo de tédio. Como o par é um modelo, você não precisa pagar pelas funções que não usa, e o recurso typedef permite atribuir nomes de tipos significativos ao código, de modo que as objeções sobre "semântica" não se sustentam.


2

Par seria uma coisa boa, para ser uma unidade básica de construção para genéricos complexos, por exemplo, isto é do meu código:

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

É exatamente o mesmo que Tuple de Haskell


1
Agora eu posso dizer, que o uso de par <A, B> torna o código menos informativa, e implementação de objetos especiais em vez de usar Pair é muito melhor
Illarion Kovalchuk

1
Melhor ou pior. Imagine que você tem uma função combinando seus dois argumentos (por exemplo, mesclando gráficos em um) e precisa armazená-lo em cache. Aqui, Pairé ideal, pois não há semântica especial. Ter um nome claro para um conceito claro é bom, mas procurar um nome onde "primeiro" e "segundo" funcionem bem não é.
Maaartinus 03/11

2

Objeto de maneira simples [] - pode ser usado como qualquer tupla de dimensão


2
Qualquer dimensão, sim. Mas: complicado de criar e não seguro para o tipo.
Michael Piefel

2

Para linguagens de programação como Java, a estrutura de dados alternativa usada pela maioria dos programadores para representar estruturas de dados pareadas é de duas matrizes e os dados são acessados ​​através do mesmo índice

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

Isso não é ideal, pois os dados devem ser unidos, mas também se mostram bastante baratos. Além disso, se o seu caso de uso exigir armazenamento de coordenadas, é melhor criar sua própria estrutura de dados.

Eu tenho algo assim na minha biblioteca

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


2

Aqui estão algumas bibliotecas que possuem vários graus de tuplas para sua conveniência:

  • JavaTuples . Tuplas do grau 1-10 é tudo o que tem.
  • JavaSlang . Tuplas do grau 0-8 e muitas outras vantagens funcionais.
  • jOOλ . Tuplas do grau 0-16 e algumas outras vantagens funcionais. (Isenção de responsabilidade, trabalho na empresa de manutenção)
  • Java funcional . Tuplas do grau 0-8 e muitas outras vantagens funcionais.

Outras bibliotecas foram mencionadas para conter pelo menos a Pairtupla.

Especificamente, no contexto da programação funcional que faz uso de muitas tipologias estruturais, em vez de tipologias nominais ( como preconizado na resposta aceita ), essas bibliotecas e suas tuplas são muito úteis.



2

outra implementação concisa do lombok

import lombok.Value;

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

1

Notei que todas as implementações de Pair espalhadas por aqui atribuem significado à ordem dos dois valores. Quando penso em um par, penso em uma combinação de dois itens em que a ordem dos dois não tem importância. Aqui está minha implementação de um par não ordenado, com hashCodee equalssubstituições para garantir o comportamento desejado nas coleções. Também clonável.

/**
 * 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;

    }

}

Essa implementação foi testada adequadamente e o uso em um conjunto e mapa foi testado.

Observe que não estou afirmando liberar isso em domínio público. Este é o código que acabei de escrever para uso em um aplicativo. Portanto, se você for usá-lo, evite fazer uma cópia direta e mexa um pouco nos comentários e nomes. Pegar a minha deriva?


3
na verdade, verificar a parte inferior de cada página: "contribuições dos usuários licenciados sob cc-wiki"
amara

Ah, eu não tinha percebido isso. Obrigado pelo aviso. Nesse caso, use o código como achar melhor sob essa licença.
#

1
A pergunta é sobre um par equivalente a C ++ - que é encomendado. Também acho que, desde que se tenha uma referência ao objeto de Pair e que sejam mutáveis, inserir pares em coleções pode levar a um comportamento indefinido.
Mr_and_Mrs_D


1

com.sun.tools.javac.util.Pair é uma implementação simples de um par. Pode ser encontrado em jdk1.7.0_51 \ lib \ tools.jar.

Além do org.apache.commons.lang3.tuple.Pair, não é apenas uma interface.


2
Porém, ninguém deve usar as APIs internas do JDK.
jpangamarca 20/09/16

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();

Imutável, apenas um par!


Uau. Outro? Tente usar o seu com Genéricos mais complexos - em algum momento, ele não conseguirá inferir os tipos apropriados. Além disso, o seguinte deve ser possível: Pair<Object, Object> pair = Pair.createPair("abc", "def")mas acho que é preciso escrever Pair.createPair((Object)"abc", (Object)"def")com seu código?
QuIT - Anony-Mousse 9/11/11

você pode substituir o método estático por isso: @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); } mas eu não sei se é uma boa prática
Bastiflew

Não, isso provavelmente estragará ainda mais as coisas. Na minha experiência, pelo menos um dos compiladores (tente o compilador java6, java7, javadoc e eclipse java) irá reclamar. O tradicional new Pair<Object, Object>("abc", "def")foi o mais confiável em meus experimentos.
QuIT - Anony-Mousse
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.