Java: existe-t-il une fonction de carte?

140

J'ai besoin d'une fonction de carte . Existe-t-il déjà quelque chose comme ça en Java?

(Pour ceux qui se demandent: je sais bien sûr implémenter moi-même cette fonction triviale ...)

Albert
la source
1
Sinon, il est trivial de se définir. Mais je suppose que Google connaît une douzaine d'implémentations?
2
Dupliqué (plutôt mieux) sur stackoverflow.com/questions/3907412/…
Chowlett
6
@Chris: Comment est-ce la même question?
Albert
1
Si la réponse à cette question est oui, elle répond également à l'autre question liée. Si la réponse est non (et cela semble être le cas), ils n'ont aucun lien.
Albert
1
À partir de Java8, c'est ce que @delnan aurait pu faire référence à leveluplunch.com/java/examples
...

Réponses:

86

Il n'y a pas de notion de fonction dans le JDK à partir de java 6.

Guava a une interface Function et la méthode fournit les fonctionnalités dont vous avez besoin.
Collections2.transform(Collection<E>, Function<E,E2>)

Exemple:

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

Production:

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

De nos jours, avec Java 8, il existe en fait une fonction de carte, donc j'écrirais probablement le code de manière plus concise:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);
Sean Patrick Floyd
la source
8
Il est intéressant de noter que si avec Guava vous pouvez le faire, vous ne voudrez peut-être pas: code.google.com/p/guava-libraries/wiki/FunctionalExplained (lisez la section "Avertissements").
Adam Parkin
2
@AdamParkin c'est vrai, mais je suis à peu près sûr que cela fait référence à des concepts fonctionnels plus avancés que celui-ci, sinon ils n'auraient pas développé les méthodes transform ( ) en premier lieu
Sean Patrick Floyd
2
En fait, non, il y a souvent un impact certain sur les performances avec les idiomes fonctionnels, c'est pourquoi ils insistent sur le fait que vous ne devriez utiliser les installations que si vous êtes certain qu'elles remplissent les deux critères énoncés: économies nettes de LOC pour la base de code dans son ensemble, et prouvées gains de performances dus à une évaluation paresseuse (ou du moins pas de performances). Ne pas argumenter contre leur utilisation, simplement indiquer que si vous allez le faire, vous devez tenir compte des avertissements des implémenteurs.
Adam Parkin
4
@SeanPatrickFloyd maintenant que Java 8 est sorti, voulez-vous mettre à jour cela avec un exemple impliquant des lambdas? J'aimeCollections2.transform(input -> Integer.toHexString(intput.intValue())
Daniel Lubarov
2
@Daniel Dans Java 8, je ne vois aucune raison de faire ça avec Guava. Au lieu de cela, j'irais chercher la réponse de Leventov
Sean Patrick Floyd
92

Depuis Java 8, il existe des options standard pour faire cela dans 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());

Voir java.util.Collection.stream()et java.util.stream.Collectors.toList().

Leventov
la source
140
C'est tellement verbeux que ça me fait mal à l'intérieur.
Natix
1
@Natix est d'accord toList(). Remplacement par un type différent:(List<R>)((List) list).replaceAll(o -> doMap((E) o));
leventov
2
Peut e -> doMap(e)être remplacé par juste doMap?
jameshfisher
3
@jameshfisher, oui, quelque chose comme foo::doMapou Foo::doMap.
leventov
9
Je suppose que c'est pourquoi Scala existe. Attendez que Java 12 ait quelque chose de lisible.
JulienD
26

Il existe une merveilleuse bibliothèque appelée Functional Java qui gère beaucoup de choses que vous voudriez que Java ait, mais ce n'est pas le cas. Là encore, il y a aussi ce merveilleux langage Scala qui fait tout ce que Java aurait dû faire mais ne le fait pas tout en étant compatible avec tout ce qui est écrit pour la JVM.

Wheaties
la source
Je suis intéressé par comment ont-ils activé la syntaxe suivante: a.map({int i => i + 42});ont-ils étendu le compilateur? ou préprocesseur ajouté?
Andrey
@Andrey - Vous pouvez leur demander vous-même ou consulter le code source pour voir comment c'est fait. Voici un lien vers la source: functionaljava.org/source
wheaties
1
@Andrey: les exemples utilisent la syntaxe de la proposition de fermetures BGGA. Bien qu'il existe un prototype en cours d'exécution, il n'est pas encore en Java «officiel».
Peter Štibraný
@Andrey: cette syntaxe fait partie d'une spécification proposée pour les fermetures en Java (voir l'avant-dernier paragraphe sur la page d'accueil). Il n'y a qu'une implémentation prototypique.
Michael Borgwardt
2
Détournement de fil Scala :( J'espère que SO ne deviendra pas comme la liste de diffusion JavaPosse;)
Jorn
9

Soyez très prudent avec Collections2.transform()de la goyave. Le plus grand avantage de cette méthode est aussi son plus grand danger: sa paresse.

Regardez la documentation de Lists.transform(), qui, je crois, s'applique également à Collections2.transform():

La fonction est appliquée paresseusement, appelée en cas de besoin. Cela est nécessaire pour que la liste retournée soit une vue, mais cela signifie que la fonction sera appliquée plusieurs fois pour des opérations en bloc comme List.contains (java.lang.Object) et List.hashCode (). Pour que cela fonctionne bien, la fonction doit être rapide. Pour éviter une évaluation paresseuse lorsque la liste renvoyée n'a pas besoin d'être une vue, copiez la liste renvoyée dans une nouvelle liste de votre choix.

Dans la documentation, Collections2.transform()ils mentionnent également que vous obtenez une vue en direct, ce changement dans la liste source affecte la liste transformée. Ce type de comportement peut entraîner des problèmes difficiles à suivre si le développeur ne réalise pas comment cela fonctionne.

Si vous voulez une "carte" plus classique, qui ne fonctionnera qu'une seule fois, alors vous feriez mieux d'utiliser FluentIterable, également depuis Guava, qui a une opération beaucoup plus simple. Voici l'exemple google pour cela:

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

transform()voici la méthode de la carte. Il utilise les mêmes fonctions <> "callbacks" que Collections.transform(). La liste que vous récupérez est en lecture seule, utilisez copyInto()pour obtenir une liste en lecture-écriture.

Sinon, bien sûr, lorsque java8 sortira avec des lambdas, ce sera obsolète.

Emmanuel Touzery
la source
2

Même si c'est une vieille question, j'aimerais montrer une autre solution:

Définissez simplement votre propre opération à l'aide des génériques java et des flux java 8:

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

Ensuite, vous pouvez écrire du code comme celui-ci:

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