Java: esiste una funzione cartografica?


140

Ho bisogno di una funzione mappa . C'è già qualcosa del genere in Java?

(Per coloro che si chiedono: ovviamente so come implementare questa banale funzione da solo ...)


1
Altrimenti, è banale definirti. Ma suppongo che Google conosca una dozzina di implementazioni?

2
Duplicato (un po 'meglio) a stackoverflow.com/questions/3907412/...
Chowlett

6
@Chris: com'è la stessa domanda?
Albert,

1
Se la risposta a questa domanda è sì, risponde anche all'altra domanda collegata. Se la risposta è no (e sembra così), sono completamente indipendenti.
Albert,

1
A partire da Java8, questo è ciò che @delnan avrebbe potuto riferirsi a leveluplunch.com/java/examples/…
Eterno

Risposte:


86

Non esiste alcuna idea di una funzione nel JDK come di Java 6.

Guava ha comunque un'interfaccia Function e il metodo fornisce le funzionalità richieste.
Collections2.transform(Collection<E>, Function<E,E2>)

Esempio:

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

Produzione:

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

In questi giorni, con Java 8, esiste in realtà una funzione di mappa, quindi probabilmente scriverei il codice in un modo più conciso:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);

8
Vale la pena notare che mentre con Guava puoi farlo, potresti non voler: code.google.com/p/guava-libraries/wiki/FunctionalExplained (leggi la sezione "Avvertenze").
Adam Parkin

2
@AdamParkin è vero, ma sono abbastanza sicuro che si riferisca a concetti funzionali più avanzati di così, altrimenti non avrebbero sviluppato i metodi transform ( ) in primo luogo
Sean Patrick Floyd

2
In realtà, no, c'è spesso un netto aumento delle prestazioni con idiomi funzionali, motivo per cui sottolineano che dovresti usare le strutture solo se sei sicuro che soddisfi i due criteri indicati: risparmi netti di LOC per la base di codice nel suo insieme e comprovato guadagni di prestazione dovuti a una valutazione pigra (o almeno non a risultati positivi). Non litigare contro il loro uso, ma solo indicare che se lo farai, dovresti prestare attenzione agli avvertimenti degli implementatori.
Adam Parkin

4
@SeanPatrickFloyd ora che Java 8 è uscito, vuoi aggiornarlo con un esempio che coinvolge lambdas? Mi piaceCollections2.transform(input -> Integer.toHexString(intput.intValue())
Daniel Lubarov,

2
@Daniel In Java 8, non vedo un motivo per farlo con Guava. Preferirei invece la risposta di leventov
Sean Patrick Floyd,

92

Da Java 8, ci sono alcune opzioni standard per farlo in 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());

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


140
È così prolisso che mi fa male dentro.
Natix,

1
@Natix sono d'accordo toList(). Sostituzione di diverso tipo:(List<R>)((List) list).replaceAll(o -> doMap((E) o));
leventov,

2
Può e -> doMap(e)essere sostituito con solo doMap?
jameshfisher,

3
@jameshfisher, sì, qualcosa del genere foo::doMapo Foo::doMap.
leventov,

9
Immagino sia per questo che Scala esiste. Attendi che Java 12 abbia qualcosa di leggibile.
JulienD,

26

C'è una meravigliosa libreria chiamata Functional Java che gestisce molte delle cose che vorresti avere Java ma non lo fa. Poi di nuovo, c'è anche questo meraviglioso linguaggio Scala che fa tutto ciò che Java avrebbe dovuto fare ma non lo fa pur essendo compatibile con qualsiasi cosa scritta per JVM.


Sono interessato a come hanno abilitato la seguente sintassi: a.map({int i => i + 42});hanno esteso il compilatore? o aggiunto preprocessore?
Andrey,

@Andrey - Puoi chiederglielo tu stesso o controllare il codice sorgente per vedere come è fatto. Ecco un link alla fonte: funzionalejava.org/source
wheaties

1
@Andrey: gli esempi usano la sintassi della proposta di chiusure BGGA. Mentre è in esecuzione il prototipo, non è ancora in Java "ufficiale".
Peter Štibraný,

@Andrey: quella sintassi fa parte di una proposta di specifica per le chiusure in Java (vedi il penultimo paragrafo sulla homepage). C'è solo un'implementazione prototipica.
Michael Borgwardt,

2
Dirottamento del thread Scala :( Spero che SO non diventerà come la mailing list di JavaPosse;)
Jorn,

9

Stai molto attento con Collections2.transform()dalla guava. Il più grande vantaggio di quel metodo è anche il suo più grande pericolo: la sua pigrizia.

Guarda la documentazione di Lists.transform(), che credo valga anche per Collections2.transform():

La funzione viene applicata pigramente, invocata quando necessario. Ciò è necessario affinché l'elenco restituito sia una vista, ma significa che la funzione verrà applicata molte volte per operazioni in blocco come List.contains (java.lang.Object) e List.hashCode (). Perché ciò funzioni bene, la funzione dovrebbe essere veloce. Per evitare una valutazione pigra quando l'elenco restituito non deve essere una vista, copiare l'elenco restituito in un nuovo elenco di propria scelta.

Anche nella documentazione di Collections2.transform()cui parlano si ottiene una vista dal vivo, che le modifiche all'elenco delle fonti influiscono sull'elenco trasformato. Questo tipo di comportamento può portare a problemi difficili da rintracciare se lo sviluppatore non si rende conto del modo in cui funziona.

Se vuoi una "mappa" più classica, che verrà eseguita una volta e una volta sola, allora stai meglio FluentIterable, anche da Guava, che ha un'operazione che è molto più semplice. Ecco l'esempio di Google per questo:

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

transform()ecco il metodo della mappa. Utilizza la stessa funzione <> "callbacks" di Collections.transform(). L'elenco che ricevi è di sola lettura, usa copyInto()per ottenere un elenco di lettura-scrittura.

Altrimenti ovviamente quando java8 esce con lambda, questo sarà obsoleto.



2

Anche se è una vecchia domanda, vorrei mostrare un'altra soluzione:

Basta definire la propria operazione usando java generics e java 8 stream:

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

Quindi puoi scrivere codice in questo modo:

List<String> hex = map(Arrays.asList(10, 20, 30, 40, 50), Integer::toHexString);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.