Mapa invertido de Java
(7)
Necesito crear un mapa inverso: seleccione valores únicos y para ellos busque claves. Parece que la única forma es repetir todos los pares clave / valor, porque entrySet devuelve el conjunto de modo que el valor no sea único. Gracias.
Parece que la única forma es repetir todos los pares clave / valor, porque entrySet devuelve el conjunto de modo que el valor no sea único.
Es una forma al menos. Aquí hay un ejemplo:
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = new HashMap<String, Integer>();
for (Integer i : map.keySet())
inverted.put(map.get(i), i);
En el caso de valores no únicos, este algoritmo mapeará el último valor encontrado en su clave. (Dado que el orden de iteración no está definido para la mayoría de los mapas, debería ser tan bueno como cualquier solución).
Si realmente desea mantener el primer valor encontrado para cada tecla, puede cambiarlo a
if (!inverted.containsKey(map.get(i)))
inverted.put(map.get(i), i);
Daría otro enfoque a este problema dando una dimensión extra: valores duplicados en EntrySet.
public static void main(String[] args) {
HashMap<Integer, String> s = new HashMap<Integer, String>();
s.put(1, "Value1");
s.put(2, "Value2");
s.put(3, "Value2");
s.put(4, "Value1");
/*
* swap goes here
*/
HashMap<String,List<Integer>> newMap = new HashMap<String, List<Integer>>();
for (Map.Entry<Integer, String> en : s.entrySet()) {
System.out.println(en.getKey() + " " + en.getValue());
if(newMap.containsKey(en.getValue())){
newMap.get(en.getValue()).add(en.getKey());
} else {
List<Integer> tmpList = new ArrayList<Integer>();
tmpList.add(en.getKey());
newMap.put(en.getValue(), tmpList);
}
}
for(Map.Entry<String, List<Integer>> entry: newMap.entrySet()){
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
El resultado será que:
1 valor1
2 Value2
3 Value2
4 Value1
Value1 [1, 4]
Value2 [2, 3]
Debe suponer que los valores pueden ser idénticos, ya que el contrato del Mapa lo permite.
En mi opinión, la mejor solución es usar una envoltura. Contendrá el valor original y agregará una identificación. Su función hashCode () dependerá de la identificación, y usted proporciona un Getter para el valor original. El código sería algo como esto:
public class MapKey
{
/**
* A new ID to differentiate equal values
*/
private int _id;
/**
* The original value now used as key
*/
private String _originalValue;
public MapKey(String originalValue)
{
_originalValue = originalValue;
//assuming some method for generating ids...
_id = getNextId();
}
public String getOriginalValue()
{
return _originalValue;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + _id;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MapKey other = (MapKey) obj;
if (_id != other._id)
return false;
return true;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("MapKey value is ");
sb.append(_originalValue);
sb.append(" with ID number ");
sb.append(_id);
return sb.toString();
}
Invertir el mapa sería algo como esto:
public Map <MapKey, Integer> invertMap(Map <Integer, String> map)
{
Map <MapKey, Integer> invertedMap = new HashMap <MapKey, Integer>();
Iterator<Entry<Integer, String>> it = map.entrySet().iterator();
while(it.hasNext())
{
//getting the old values (to be reversed)
Entry<Integer, String> entry = it.next();
Integer oldKey = entry.getKey();
String oldValue = entry.getValue();
//creating the new MapKey
MapKey newMapKey = new MapKey(oldValue);
invertedMap.put(newMapKey, oldKey);
}
return invertedMap;
}
Imprimiendo los valores algo como esto:
for(MapKey key : invertedMap.keySet())
{
System.out.println(key.toString() + " has a new value of " + invertedMap.get(key));
}
No se prueba ninguno de estos códigos, pero creo que es la mejor solución, ya que utiliza el diseño de herencia OO en lugar de las comprobaciones de estilo "c" y le permite mostrar todas las claves y valores originales.
Eche un vistazo a Google Guava BiMap .
Ejemplo de uso
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = HashBiMap.create(map).inverse();
Los valores en un mapa pueden no ser únicos. Pero si lo son (en su caso) puede hacer lo que escribió en su pregunta y crear un método genérico para convertirlo:
private static <V, K> Map<V, K> invert(Map<K, V> map) {
Map<V, K> inv = new HashMap<V, K>();
for (Entry<K, V> entry : map.entrySet())
inv.put(entry.getValue(), entry.getKey());
return inv;
}
Java 8:
public static <V, K> Map<V, K> invert(Map<K, V> map) {
return map.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getValue, c -> c.getKey()));
}
Ejemplo de uso:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Hello", 0);
map.put("World!", 1);
Map<Integer, String> inv = invert(map);
System.out.println(inv); // outputs something like "{0=Hello, 1=World!}"
}
Nota al margen: el método put(.., ..)
devolverá el valor "antiguo" para una clave. Si no es nulo, puede lanzar una new IllegalArgumentException("Map values must be unique")
o algo así.
Para obtener una forma invertida de un mapa dado en java 8:
public static <K, V> Map<V, K> inverseMap(Map<K, V> sourceMap) {
return sourceMap.entrySet().stream().collect(
Collectors.toMap(Entry::getValue, Entry::getKey,
(a, b) -> a) //if sourceMap has duplicate values, keep only first
);
}
Ejemplo de uso
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = inverseMap(map);