Sé cómo "transformar" un Java simple List
de Y
-> Z
, es decir:
List<String> x;
List<Integer> y = x.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Ahora me gustaría hacer básicamente lo mismo con un mapa, es decir:
INPUT:
{
"key1" -> "41", // "41" and "42"
"key2" -> "42 // are Strings
}
OUTPUT:
{
"key1" -> 41, // 41 and 42
"key2" -> 42 // are Integers
}
La solución no debe limitarse a String
-> Integer
. Al igual que en el List
ejemplo anterior, me gustaría llamar a cualquier método (o constructor).
java
mapreduce
java-8
java-stream
collectors
Benjamin M
fuente
fuente
e -> e.getKey()
conMap.Entry::getKey
. Pero eso es una cuestión de gusto / estilo de programación.Aquí hay algunas variaciones en la respuesta de Sotirios Delimanolis , que fue bastante buena para empezar (+1). Considera lo siguiente:
Un par de puntos aquí. Primero está el uso de comodines en los genéricos; Esto hace que la función sea algo más flexible. Sería necesario un comodín si, por ejemplo, desea que el mapa de salida tenga una clave que sea una superclase de la clave del mapa de entrada:
(También hay un ejemplo para los valores del mapa, pero es realmente artificial, y admito que tener el comodín delimitado para Y solo ayuda en casos extremos).
Un segundo punto es que, en lugar de ejecutar la secuencia sobre el mapa de entrada
entrySet
, la ejecuté sobrekeySet
. Esto hace que el código sea un poco más limpio, creo, a costa de tener que recuperar valores del mapa en lugar de hacerlo desde la entrada del mapa. Por cierto, inicialmente tuvekey -> key
como primer argumentotoMap()
y esto falló con un error de inferencia de tipo por alguna razón. Cambiando a(X key) -> key
trabajado, como lo hizoFunction.identity()
.Todavía otra variación es la siguiente:
Esto utiliza en
Map.forEach()
lugar de secuencias. Esto es aún más simple, creo, porque prescinde de los coleccionistas, que son algo torpes de usar con los mapas. La razón es queMap.forEach()
proporciona la clave y el valor como parámetros separados, mientras que la secuencia solo tiene un valor, y debe elegir si usar la clave o la entrada del mapa como ese valor. En el lado negativo, esto carece de la bondad rica y fluida de los otros enfoques. :-)fuente
Function.identity()
puede parecer genial, pero dado que la primera solución requiere una búsqueda de mapa / hash para cada entrada, mientras que todas las demás soluciones no lo hacen, no lo recomendaría.Una solución genérica como esta
Ejemplo
fuente
La función de guayaba
Maps.transformValues
es lo que está buscando, y funciona muy bien con expresiones lambda:fuente
java.util.Function
, tiene dos opciones. 1. Evite el problema utilizando una lambda para permitir que la inferencia de tipo Java lo resuelva. 2. Utilice una referencia de método como javaFunction :: apply para producir una nueva lambda que pueda deducir la inferencia de tipos.¿Tiene que ser absolutamente funcional y fluido al 100%? Si no, ¿qué tal esto, que es lo más corto posible?
( si puedes vivir con la vergüenza y la culpa de combinar secuencias con efectos secundarios )
fuente
La biblioteca My StreamEx que mejora la API de flujo estándar proporciona una
EntryStream
clase que se adapta mejor para transformar mapas:fuente
Una alternativa que siempre existe con fines de aprendizaje es construir su recopilador personalizado a través de Collector.of (), aunque toMap () JDK collector aquí es sucinto (+1 aquí ).
fuente
map2.entrySet().forEach(entry -> { if (map1.containsKey(entry.getKey())) { map1.get(entry.getKey()).merge(entry.getValue()); } else { map1.put(entry.getKey(),entry.getValue()); } }); return map1
o se perderán valores al reducirlo.Si no le importa usar bibliotecas de terceros, mi libra cyclops-react tiene extensiones para todos los tipos de colección JDK , incluido Map . Simplemente podemos transformar el mapa directamente usando el operador 'mapa' (por defecto, el mapa actúa sobre los valores en el mapa).
bimap se puede usar para transformar las claves y los valores al mismo tiempo
fuente
La solución declarativa y más simple sería:
yourMutableMap.replaceAll ((key, val) -> return_value_of_bi_your_function); Nótese bien. tenga en cuenta que está modificando el estado de su mapa. Entonces esto puede no ser lo que quieres.
Saludos a: http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/
fuente