Java: existe uma função de mapa?

140

Eu preciso de uma função de mapa . Já existe algo assim em Java?

(Para aqueles que se perguntam: é claro que sei como implementar essa função trivial ...)

Albert
fonte
1
Caso contrário, é trivial se definir. Mas suponho que o Google conheça uma dúzia de implementações?
2
Duplicado (um pouco melhor) em stackoverflow.com/questions/3907412/…
Chowlett 11/11/10
6
@ Chris: Como é a mesma pergunta?
Albert
1
Se a resposta a esta pergunta for sim, ela também responde à outra pergunta vinculada. Se a resposta for não (e parece que sim), elas são completamente independentes.
Albert
1
A partir de Java8, Isto é o que @delnan poderia ter sido referindo-se leveluplunch.com/java/examples/...
Eternalcode

Respostas:

86

Não há noção de uma função no JDK a partir do java 6.

A goiaba possui uma interface Function e o método fornece a funcionalidade que você precisa.
Collections2.transform(Collection<E>, Function<E,E2>)

Exemplo:

// example, converts a collection of integers to their
// hexadecimal string representations
final Collection<Integer> input = Arrays.asList(10, 20, 30, 40, 50);
final Collection<String> output =
    Collections2.transform(input, new Function<Integer, String>(){

        @Override
        public String apply(final Integer input){
            return Integer.toHexString(input.intValue());
        }
    });
System.out.println(output);

Resultado:

[a, 14, 1e, 28, 32]

Hoje em dia, com o Java 8, existe na verdade uma função de mapa, então eu provavelmente escreveria o código de uma maneira mais concisa:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);
Sean Patrick Floyd
fonte
8
Vale a pena notar que, embora com o Guava, você possa fazer isso, convém: code.google.com/p/guava-libraries/wiki/FunctionalExplained (leia a seção "Advertências").
Adam Parkin
2
@AdamParkin verdade, mas eu tenho certeza que se refere a conceitos funcionais mais avançados do que este, caso contrário não teria se desenvolvido a transformar ( ) métodos em primeiro lugar
Sean Patrick Floyd
2
Na verdade, não, geralmente há um desempenho definido com idiomas funcionais, e é por isso que eles enfatizam que você só deve usar as instalações se tiver certeza de que atende aos dois critérios descritos: economia líquida de LOC para a base de código como um todo e comprovada ganhos de desempenho devido a avaliação lenta (ou pelo menos não resultados de desempenho). Não argumentando contra o uso deles, apenas indicando que, se você for, deve prestar atenção aos avisos dos implementadores.
9118 Adam Parkin
4
@SeanPatrickFloyd agora que o Java 8 está fora, deseja atualizá-lo com um exemplo envolvendo lambdas? ComoCollections2.transform(input -> Integer.toHexString(intput.intValue())
Daniel Lubarov 04/04
2
@ Daniel No Java 8, não vejo uma razão para fazer isso com o Guava. Em vez disso eu iria para a resposta de leventov
Sean Patrick Floyd
92

Desde o Java 8, existem algumas opções padrão para fazer isso no JDK:

Collection<E> in = ...
Object[] mapped = in.stream().map(e -> doMap(e)).toArray();
// or
List<E> mapped = in.stream().map(e -> doMap(e)).collect(Collectors.toList());

Veja java.util.Collection.stream()e java.util.stream.Collectors.toList().

leventov
fonte
140
Isso é muito detalhado que me machuca por dentro.
Natix
1
@Natix concorda sobre toList(). Substituindo para outro tipo:(List<R>)((List) list).replaceAll(o -> doMap((E) o));
leventov
2
Pode e -> doMap(e)ser substituído por apenas doMap?
Jameshfisher
3
@jameshfisher, sim, algo como foo::doMapou Foo::doMap.
precisa saber é o seguinte
9
Eu acho que é por isso que Scala existe. Aguarde o Java 12 ter algo legível.
precisa saber é o seguinte
26

Existe uma biblioteca maravilhosa chamada Java funcional que lida com muitas das coisas que você gostaria que o Java tivesse, mas não. Por outro lado, também há esta maravilhosa linguagem Scala, que faz tudo o que Java deveria ter feito, mas ainda não é compatível com qualquer coisa escrita para a JVM.

wheaties
fonte
Estou interessado em como eles habilitaram a seguinte sintaxe: a.map({int i => i + 42});eles estenderam o compilador? ou pré-processador adicionado?
Andrey
@ Andrey - Você pode perguntar a eles ou verificar o código-fonte para ver como é feito. Aqui está um link para a fonte: functionaljava.org/source
wheaties
1
@Andrey: exemplos usam sintaxe da proposta de fechamento de BGGA. Embora exista um protótipo em execução, ele ainda não está em Java 'oficial'.
Peter Štibraný
@ Andrey: essa sintaxe faz parte de uma especificação proposta para fechamentos em Java (consulte o penúltimo parágrafo na página inicial). Existe apenas uma implementação prototípica.
Michael Borgwardt
2
Scala fio hijack :( Espero que não será como a lista de discussão JavaPosse;)
Jorn
9

Tenha muito cuidado com a Collections2.transform()goiaba. A maior vantagem desse método também é seu maior perigo: sua preguiça.

Veja a documentação da Lists.transform()qual acredito que também se aplica a Collections2.transform():

A função é aplicada preguiçosamente, invocada quando necessário. Isso é necessário para que a lista retornada seja uma visualização, mas significa que a função será aplicada várias vezes para operações em massa como List.contains (java.lang.Object) e List.hashCode (). Para que isso funcione bem, a função deve ser rápida. Para evitar uma avaliação lenta quando a lista retornada não precisar ser uma visualização, copie a lista retornada para uma nova lista de sua escolha.

Também na documentação Collections2.transform()mencionada, você obtém uma visualização ao vivo, que a mudança na lista de fontes afeta a lista transformada. Esse tipo de comportamento pode levar a problemas difíceis de rastrear se o desenvolvedor não perceber o modo como funciona.

Se você deseja um "mapa" mais clássico, que será executado uma vez e apenas uma vez, é melhor usar FluentIterabletambém o Guava, que possui uma operação muito mais simples. Aqui está o exemplo do google para isso:

FluentIterable
       .from(database.getClientList())
       .filter(activeInLastMonth())
       .transform(Functions.toStringFunction())
       .limit(10)
       .toList();

transform()aqui está o método do mapa. Ele usa a mesma função <> "retornos de chamada" que Collections.transform(). A lista que você voltar é somente leitura, use copyInto()para obter uma lista de leitura e gravação.

Caso contrário, é claro que quando o java8 sair com lambdas, isso será obsoleto.

Emmanuel Touzery
fonte
2

Mesmo que seja uma pergunta antiga, gostaria de mostrar outra solução:

Basta definir sua própria operação usando java generics e java 8 streams:

public static <S, T> List<T> map(Collection<S> collection, Function<S, T> mapFunction) {
   return collection.stream().map(mapFunction).collect(Collectors.toList());
}

Então você pode escrever um código como este:

List<String> hex = map(Arrays.asList(10, 20, 30, 40, 50), Integer::toHexString);
IPP Nerd
fonte