Estou brincando com operações funcionais preguiçosas no Java SE 8 e quero map
um índice i
para um par / tupla (i, value[i])
, depois com filter
base no segundo value[i]
elemento e, finalmente, produzir apenas os índices.
Ainda devo sofrer o seguinte: Qual é o equivalente do par C ++ <L, R> em Java? na nova era ousada de lambdas e córregos?
Atualização: apresentei um exemplo bastante simplificado, que tem uma solução interessante oferecida pelo @dkatzel em uma das respostas abaixo. No entanto, não generaliza. Portanto, deixe-me adicionar um exemplo mais geral:
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))
);
}
}
Isso fornece uma saída incorreta,[0, 0, 0]
que corresponde às contagens das três colunas que são todas false
. O que eu preciso são os índices dessas três colunas. A saída correta deve ser [0, 2, 4]
. Como posso obter esse resultado?
fonte
AbstractMap.SimpleImmutableEntry<K,V>
há anos ... Mas de qualquer maneira, em vez de mapeamentoi
para(i, value[i])
apenas para filtrar porvalue[i]
e mapeamento de volta parai
: porque não basta filtrovalue[i]
em primeiro lugar, sem o mapeamento?i
no fluxo. Eu também precisovalue[i]
dos critérios. É por isso que eu preciso(i, value[i])
[0, 2, 4]
?Respostas:
ATUALIZAÇÃO: Esta resposta é uma resposta à pergunta original, o Java SE 8 possui pares ou tuplas? (E implicitamente, se não, por que não?) O OP atualizou a pergunta com um exemplo mais completo, mas parece que pode ser resolvido sem o uso de qualquer tipo de estrutura de pares. [Nota do OP: aqui está a outra resposta correta .]
A resposta curta é não. Você precisa rolar por conta própria ou trazer uma das várias bibliotecas que a implementa.
Ter uma
Pair
aula no Java SE foi proposto e rejeitado pelo menos uma vez. Veja este tópico de discussão em uma das listas de discussão do OpenJDK. As compensações não são óbvias. Por um lado, existem muitas implementações de pares em outras bibliotecas e no código do aplicativo. Isso demonstra uma necessidade e a inclusão dessa classe no Java SE aumentará a reutilização e o compartilhamento. Por outro lado, ter uma classe Pair aumenta a tentação de criar estruturas de dados complicadas a partir de pares e coleções sem criar os tipos e abstrações necessários. (Essa é uma paráfrase da mensagem de Kevin Bourillion desse tópico.)Eu recomendo que todos leiam todo esse tópico de email. É notavelmente perspicaz e não tem flamage. É bastante convincente. Quando começou, pensei: "Sim, deveria haver uma classe Pair no Java SE", mas quando o segmento chegou ao fim, eu havia mudado de idéia.
Observe, no entanto, que o JavaFX possui a classe javafx.util.Pair . As APIs do JavaFX evoluíram separadamente das APIs do Java SE.
Como se pode ver na questão vinculada Qual é o equivalente do par C ++ em Java? existe um espaço de design bastante grande em torno do que aparentemente é uma API tão simples. Os objetos devem ser imutáveis? Eles devem ser serializáveis? Eles deveriam ser comparáveis? A aula deve ser final ou não? Os dois elementos devem ser ordenados? Deve ser uma interface ou uma classe? Por que parar em pares? Por que não triplas, quads ou N-tuplas?
E, claro, há a inevitável nomeação de bikeshed para os elementos:
Um grande problema que quase não foi mencionado é o relacionamento dos pares com os primitivos. Se você possui um
(int x, int y)
dado que representa um ponto no espaço 2D, representá-lo comoPair<Integer, Integer>
consome três objetos em vez de duas palavras de 32 bits. Além disso, esses objetos devem residir no heap e incorrem em sobrecarga do GC.Parece claro que, como Streams, seria essencial haver especializações primitivas para Pares. Queremos ver:
Mesmo um
IntIntPair
ainda exigiria um objeto na pilha.Naturalmente, elas lembram a proliferação de interfaces funcionais no
java.util.function
pacote no Java SE 8. Se você não quer uma API inchada, quais você deixaria de fora? Você também pode argumentar que isso não é suficiente e que especializações para, por exemplo,Boolean
devem ser adicionadas também.Meu sentimento é que se o Java tivesse adicionado uma classe Pair há muito tempo, seria simples, ou até simplista, e não teria satisfeito muitos dos casos de uso que estamos imaginando agora. Considere que se o Pair tivesse sido adicionado no período de tempo do JDK 1.0, provavelmente teria sido mutável! (Veja java.util.Date.) As pessoas ficariam felizes com isso? Meu palpite é que, se houvesse uma classe Pair em Java, seria meio que não muito útil e todo mundo continuaria se adaptando para satisfazer suas necessidades, haveria várias implementações Pair e Tuple em bibliotecas externas, e as pessoas ainda estariam discutindo / discutindo sobre como corrigir a classe Pair do Java. Em outras palavras, meio que no mesmo lugar em que estamos hoje.
Enquanto isso, algum trabalho está em andamento para resolver a questão fundamental, que é o melhor suporte na JVM (e eventualmente na linguagem Java) para tipos de valor . Consulte este documento Estado dos valores . Este é um trabalho preliminar e especulativo, e cobre apenas questões da perspectiva da JVM, mas já tem uma boa quantidade de pensamento por trás. Obviamente, não há garantias de que isso chegue ao Java 9, ou que entre em qualquer lugar, mas mostra a direção atual de se pensar neste tópico.
fonte
Pair<T,U>
. Como os genéricos devem ser do tipo de referência. Quaisquer primitivas serão colocadas em caixa quando forem armazenadas. Para armazenar primitivos, você realmente precisa de uma classe diferente.valueOf
deveriam ter sido a única maneira de obter uma instância em caixa. Mas eles estão lá desde o Java 1.0 e provavelmente não valem a pena tentar mudar neste momento.Pair
ouTuple
classe com um método de fábrica, criando as classes de especialização necessárias (com armazenamento otimizado) de forma transparente em segundo plano. No final, os lambdas fazem exatamente isso: eles podem capturar um número arbitrário de variáveis do tipo arbitrário. E agora uma imagem de suporte de linguagem que permite criar a classe tupla apropriado em tempo de execução desencadeada por umainvokedynamic
instrução ...invokedynamic
fábrica baseada semelhante à criação de lambda, essa adaptação posterior não seria problema. A propósito, as lambdas também não têm identidade. Conforme explicitamente declarado, a identidade que você pode perceber hoje é um artefato da implementação atual.Você pode dar uma olhada nessas classes internas:
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
fonte
SimpleImmutableEntry
apenas garante que as referências armazenadas noEntry
arquivo não sejam alteradas, e que os campos dos objetoskey
e vinculadosvalue
(ou dos objetos aos quais eles vinculam) não sejam alterados.Infelizmente, o Java 8 não introduziu pares ou tuplas. Você sempre pode usar org.apache.commons.lang3.tuple, é claro (que pessoalmente uso em combinação com Java 8) ou pode criar seus próprios wrappers. Ou use o Maps. Ou coisas assim, conforme explicado na resposta aceita para a pergunta à qual você vinculou.
ATUALIZAÇÃO: O JDK 14 está introduzindo registros como um recurso de visualização. Essas não são tuplas, mas podem ser usadas para salvar muitos dos mesmos problemas. No seu exemplo específico acima, isso pode ser algo como isto:
Quando compilado e executado com o JDK 14 (no momento da redação, este é um build de acesso antecipado) usando o
--enable-preview
sinalizador, você obtém o seguinte resultado:fonte
Parece que o exemplo completo pode ser resolvido sem o uso de qualquer tipo de estrutura de pares. A chave é filtrar os índices da coluna, com o predicado verificando a coluna inteira, em vez de mapear os índices da coluna para o número de
false
entradas nessa coluna.O código que faz isso está aqui:
Isso resulta em resultado do
[0, 2, 4]
qual acho que o resultado correto solicitado pelo OP.Observe também a
boxed()
operação que coloca osint
valores emInteger
objetos. Isso permite que você use otoList()
coletor preexistente , em vez de precisar escrever funções do coletor que fazem o boxe.fonte
true
). Consequentemente, aceitarei sua outra resposta como correta, mas também aponto para essa! Muito obrigado :)O Vavr (anteriormente chamado Javaslang) ( http://www.vavr.io ) também fornece tuplas (até 8). Aqui está o javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .
Este é um exemplo simples:
Por que o JDK em si não veio com um tipo simples de tuplas até agora é um mistério para mim. Escrever classes de wrapper parece ser um negócio diário.
fonte
Desde o Java 9, você pode criar instâncias
Map.Entry
mais fáceis do que antes:Map.entry
retorna um não modificávelEntry
e proíbe nulos.fonte
Como você se preocupa apenas com os índices, não precisa mapear para tuplas. Por que não escrever um filtro que use os elementos de pesquisa em sua matriz?
fonte
Sim.
Map.Entry
pode ser usado como umPair
.Infelizmente, isso não ajuda nos fluxos do Java 8, pois o problema é que, embora os lambdas possam aceitar vários argumentos, a linguagem Java apenas permite retornar um único valor (objeto ou tipo primitivo). Isso implica que, sempre que você tiver um fluxo, você receberá um único objeto da operação anterior. Isso é uma falta na linguagem Java, porque se vários valores de retorno fossem suportados E os fluxos os suportassem, poderíamos ter tarefas não triviais muito mais agradáveis feitas por fluxos.
Até então, há pouco uso.
EDIT 2018-02-12: Enquanto trabalhava em um projeto, escrevi uma classe auxiliar que ajuda a lidar com o caso especial de ter um identificador no início do fluxo que você precisa mais tarde, mas a parte do fluxo intermediária não o conhece. Até que eu possa liberá-lo, ele estará disponível no IdValue.java com um teste de unidade no IdValueTest.java
fonte
O Eclipse Collections possui
Pair
e todas as combinações de pares primitivos / de objetos (para todas as oito primitivas).A
Tuples
fábrica pode criar instânciasPair
e osPrimitiveTuples
fábrica pode ser usada para criar todas as combinações de pares primitivo / objeto.Nós os adicionamos antes do lançamento do Java 8. Eles foram úteis para implementar Iteradores de chave / valor para nossos mapas primitivos, que também suportamos em todas as combinações primitivas / objetos.
Se você deseja adicionar a sobrecarga extra da biblioteca, pode usar a solução aceita por Stuart e coletar os resultados em uma primitiva
IntList
para evitar o boxe. Adicionamos novos métodos no Eclipse Collections 9.0 para permitir queInt/Long/Double
coleções sejam criadas a partir doInt/Long/Double
Streams.Nota: Sou um colaborador das Coleções Eclipse.
fonte