2010-10-11 15 views
111

Necesito una función map. ¿Ya hay algo como esto en Java?Java: ¿hay una función de mapa?

(Para aquellos que se preguntan: yo, por supuesto, saber cómo implementar esta función trivial mismo ...)

+0

Si no, es trivial definirse. Pero supongo que Google sabe una docena de implementaciones? – delnan

+2

Duplicado (bastante mejor) en http://stackoverflow.com/questions/3907412/java-general-function-x-y-interface – Chowlett

+5

@Chris: ¿Cómo es la misma pregunta? – Albert

Respuesta

73

no hay noción de una función en el JDK de Java a partir del 6.

Guava tiene una interfaz Function y aunque el método
Collections2.transform(Collection<E>, Function<E,E2>)
proporciona la funcionalidad que necesite.

Ejemplo:

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

Salida:

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

En estos días, con Java 8, no es en realidad una función de mapa, así que probablemente iba a escribir la código de una manera más concisa:

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

Vale la pena señalar que, si bien con Guava * puede * hacer esto, es posible que no desee: http: //code.google.com/p/guava-libraries/wiki/FunctionalExplained (lea la sección "Advertencias"). –

+2

@AdamParkin cierto, pero estoy bastante seguro de que se refiere a conceptos funcionales más avanzados que este, de lo contrario no habrían desarrollado los métodos * transform (*) en primer lugar –

+2

En realidad, no, a menudo hay un rendimiento definido golpear con modismos funcionales, por lo que destacan que solo debe utilizar las instalaciones si está seguro de que cumple con los dos criterios descritos: ahorros netos de LOC para la base de código como un todo, y ganancias de rendimiento * probadas debido a la evaluación perezosa (o al menos no éxitos de rendimiento).No argumentando en contra del uso de ellos, simplemente indicando que si vas a hacerlo, debes prestar atención a las advertencias de los implementadores. –

21

Hay una maravillosa biblioteca llamada Functional Java que se ocupa de muchas de las cosas que te gustaría tener, pero Java no es así Por otra parte, también está este maravilloso lenguaje de Scala, que hace todo lo que Java debería haber hecho, pero no lo hace, sin dejar de ser compatible con cualquier cosa escrita para la JVM.

+0

Estoy interesado en cómo habilitaron siguiendo la sintaxis: 'a.map ({int i => i + 42});' ¿extendieron el compilador? o preprocesador agregado? – Andrey

+0

@Andrey: puede preguntárselo usted mismo o consultar el código fuente para ver cómo se hace. Aquí hay un enlace a la fuente: http://functionaljava.org/source/ – wheaties

+1

@Andrey: los ejemplos usan la sintaxis de la propuesta de cierres BGGA. Si bien hay un prototipo en ejecución, aún no está en Java 'oficial'. –

2

Ésta es otra lib funcional con el que se puede usar el mapa: http://code.google.com/p/totallylazy/

sequence(1, 2).map(toString); // lazily returns "1", "2" 
+0

Aquí hay una guía sobre cómo usar TotallyLazy: http://intrepidis.blogspot.com/2013/07/using-totallylazy-functional-library.html –

9

tener mucho cuidado con Collections2.transform() de guayaba. La mayor ventaja de ese método es también su mayor peligro: su pereza.

vistazo a la documentación de Lists.transform(), que creo que se aplica también a Collections2.transform():

La función se aplica con pereza, invoca cuando sea necesario. Esto es necesario para que la lista devuelta sea una vista, pero significa que la función se aplicará muchas veces para operaciones masivas como List.contains (java.lang.Object) y List.hashCode(). Para que esto funcione al , la función debe ser rápida. Para evitar la evaluación diferida cuando la lista devuelta no necesita ser una vista, copie la lista devuelta en una nueva lista de su elección.

También en la documentación de Collections2.transform() Mencionan se obtiene una vista en vivo, que el cambio en la lista de origen afecta la lista transformado. Este tipo de comportamiento puede llevar a problemas difíciles de seguir si el desarrollador no se da cuenta de la forma en que funciona.

Si quiere un "mapa" más clásico, que se ejecutará una sola vez, entonces estará mejor con FluentIterable, también de Guava, que tiene una operación que es mucho más simple.Aquí está el ejemplo de Google para ello:

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

transform() aquí es el método de mapa. Utiliza la misma función <> "devoluciones de llamada" como Collections.transform(). La lista que obtiene es de solo lectura, use copyInto() para obtener una lista de lectura y escritura.

De lo contrario, por supuesto, cuando java8 sale con lambdas, esto será obsoleto.

76

Desde Java 8, hay algunas opciones estándar de hacer esto en el 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()); 

Ver java.util.Collection.stream() y java.util.stream.Collectors.toList().

+97

Esto es tanto verboso que me duele el interior. – Natix

+0

@Natix si la colección es ArrayList y no la necesita después de la asignación, es más simple y más eficiente ['ArrayList.replaceAll'] (http://download.java.net/jdk8/docs/api/java/ util/ArrayList.html # replaceAll-java.util.function.UnaryOperator-). – leventov

+0

@Natix detallado, pero ágil. 'stream()' o 'parallelStream()', recopilando en List o Set, etc. Pero estoy de acuerdo, debe haber atajos para la mayoría de los casos de uso común. – leventov

Cuestiones relacionadas