Java: ¿hay una función de mapa?

140

Necesito una función de mapa . ¿Hay algo como esto en Java ya?

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

Albert
fuente
1
Si no, es trivial definirse a sí mismo. ¿Pero supongo que Google conoce una docena de implementaciones?
2
Duplicado (bastante mejor) en stackoverflow.com/questions/3907412/…
Chowlett el
66
@ Chris: ¿Cómo es la misma pregunta?
Albert
1
Si la respuesta a esta pregunta es sí, responde también a la otra pregunta vinculada. Si la respuesta es no (y parece que sí), no tienen ninguna relación.
Albert
1
A partir de Java8, esto es a lo que @delnan podría haberse referido con leveluplunch.com/java/examples/…
Eternalcode

Respuestas:

86

No hay una noción de una función en el JDK a partir de java 6.

Sin embargo, Guava tiene una interfaz de función y el método proporciona la funcionalidad que necesita.
Collections2.transform(Collection<E>, Function<E,E2>)

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, en realidad hay una función de mapa, por lo que probablemente escribiría el código de una manera más concisa:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);
Sean Patrick Floyd
fuente
8
Vale la pena señalar que si bien con Guava puede hacer esto, es posible que no desee: code.google.com/p/guava-libraries/wiki/FunctionalExplained (lea la sección "Advertencias").
Adam Parkin
2
@AdamParkin es cierto, pero estoy bastante seguro de que se refiere a conceptos funcionales más avanzados que esto, de lo contrario no habrían desarrollado los métodos transform ( ) en primer lugar
Sean Patrick Floyd
2
En realidad, no, a menudo hay un impacto definitivo en el rendimiento con modismos funcionales, por lo que enfatizan que solo debe usar las instalaciones si está seguro de que cumple con los dos criterios descritos: ahorro neto de LOC para la base de código en su conjunto, y demostrado ganancias de rendimiento debido a una evaluación perezosa (o al menos no a resultados de rendimiento). No discutiendo contra el uso de ellos, solo indicando que si vas a hacerlo, debes prestar atención a las advertencias de los implementadores.
Adam Parkin
44
@SeanPatrickFloyd ahora que Java 8 está fuera, ¿quieres actualizar esto con un ejemplo que involucra lambdas? Me gustaCollections2.transform(input -> Integer.toHexString(intput.intValue())
Daniel Lubarov
2
@Daniel En Java 8, no veo una razón para hacerlo con Guava. En cambio, iría por la respuesta de leventov
Sean Patrick Floyd
92

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

leventov
fuente
140
Esto es tan detallado que me duele por dentro.
Natix
1
@Natix está de acuerdo toList(). Sustitución a otro tipo:(List<R>)((List) list).replaceAll(o -> doMap((E) o));
leventov
2
¿Se e -> doMap(e)puede reemplazar por solo doMap?
jameshfisher
3
@jameshfisher, sí, algo así como foo::doMapo Foo::doMap.
leventov
9
Supongo que por eso existe Scala. Espere a que Java 12 tenga algo legible.
JulienD
26

Hay una biblioteca maravillosa llamada Functional Java que maneja muchas de las cosas que le gustaría que Java tuviera pero no las tiene. Por otra parte, también está este maravilloso lenguaje Scala que hace todo lo que Java debería haber hecho, pero no lo hace mientras sigue siendo compatible con cualquier cosa escrita para la JVM.

Wheaties
fuente
Estoy interesado en cómo habilitaron la siguiente sintaxis: a.map({int i => i + 42});¿extendieron el compilador? o preprocesador agregado?
Andrey
@Andrey: puedes preguntarles eso tú mismo o consultar el código fuente para ver cómo se hace. Aquí hay un enlace a la fuente: functionaljava.org/source
wheaties
1
@Andrey: los ejemplos usan la sintaxis de la propuesta de cierre de BGGA. Si bien hay un prototipo en ejecución, todavía no está en Java 'oficial'.
Peter Štibraný
@Andrey: esa sintaxis es parte de una especificación propuesta para cierres en Java (consulte el penúltimo párrafo en la página de inicio). Solo hay una implementación prototípica.
Michael Borgwardt
2
Scala thread secuestro :( Espero que SO no sea como la lista de correo de JavaPosse;)
Jorn
9

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

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

La función se aplica perezosamente, se invoca cuando es 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 bien, la función debe ser rápida. Para evitar una 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()que mencionan que obtienes una vista en vivo, ese cambio en la lista fuente afecta la lista transformada. Este tipo de comportamiento puede conducir a problemas difíciles de rastrear si el desarrollador no se da cuenta de cómo funciona.

Si quieres un "mapa" más clásico, que se ejecutará una vez y solo una vez, entonces es mejor que 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í está el método de mapa. Utiliza la misma función <> "devoluciones de llamada" que Collections.transform(). Sin embargo, la lista que obtiene es de solo lectura, úsela copyInto()para obtener una lista de lectura y escritura.

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

Emmanuel Touzery
fuente
2

Aunque es una vieja pregunta, me gustaría mostrar otra solución:

Simplemente defina su propia operación utilizando java generics y 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());
}

Entonces puedes escribir código como este:

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