implementación mapa con claves duplicadas
-
21-08-2019 - |
Pregunta
Quiero tener un mapa con claves duplicadas.
Sé que hay muchas implementaciones de mapa (Eclipse me muestra alrededor de 50), por lo que apuesto a que debe haber uno que permite esto. Sé que es fácil de escribir su propio mapa que hace esto, pero yo prefiero utilizar alguna solución existente.
Tal vez algo en Commons-colecciones o Google-colecciones?
Solución
Usted está buscando un multimap, y de hecho ambos Commons-colecciones y guayaba tiene varias implementaciones para eso. Multimapas permiten múltiples teclas mediante el mantenimiento de una colección de valores por clave, es decir, se puede poner un solo objeto en el mapa, pero puede recuperar una colección.
Si puede utilizar Java 5, preferiría Guava Multimap
ya que es genéricos para notificaciones.
Otros consejos
No necesitamos depender de biblioteca externa de Google Colecciones. Simplemente puede aplicar el siguiente mapa:
Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();
public static void main(String... arg) {
// Add data with duplicate keys
addValues("A", "a1");
addValues("A", "a2");
addValues("B", "b");
// View data.
Iterator it = hashMap.keySet().iterator();
ArrayList tempList = null;
while (it.hasNext()) {
String key = it.next().toString();
tempList = hashMap.get(key);
if (tempList != null) {
for (String value: tempList) {
System.out.println("Key : "+key+ " , Value : "+value);
}
}
}
}
private void addValues(String key, String value) {
ArrayList tempList = null;
if (hashMap.containsKey(key)) {
tempList = hashMap.get(key);
if(tempList == null)
tempList = new ArrayList();
tempList.add(value);
} else {
tempList = new ArrayList();
tempList.add(value);
}
hashMap.put(key,tempList);
}
Por favor, asegúrese de ajustar el código.
Multimap<Integer, String> multimap = ArrayListMultimap.create();
multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");
multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");
multimap.put(3, "A");
System.out.println(multimap.get(1));
System.out.println(multimap.get(2));
System.out.println(multimap.get(3));
La salida es:
[A,B,C,A]
[A,B,C]
[A]
Nota: tenemos que importar archivos de la biblioteca
.http://www.java2s.com/Code/Jar/g/ Downloadgooglecollectionsjar.htm
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
o https://commons.apache.org/proper/commons-collections /download_collections.cgi
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
Usted podría simplemente pasa una matriz de valores para el valor en un HashMap regular, simulando así duplicados de las llaves, y sería hasta usted para decidir qué datos desea utilizar.
Es posible que también acaba de utilizar un MultiMap , aunque no me gusta la idea de duplicados de las llaves a mí mismo.
Si desea iterar sobre una lista de pares de valores-clave (como los escribió en el comentario), a continuación, una lista o una matriz debería ser mejor. En primer lugar combinar sus claves y valores:
public class Pair
{
public Class1 key;
public Class2 value;
public Pair(Class1 key, Class2 value)
{
this.key = key;
this.value = value;
}
}
Reemplazar Clase 1 y Clase 2 con los tipos que desea utilizar para las claves y valores.
Ahora usted puede ponerlos en una matriz o una lista e iterar sobre ellos:
Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
...
}
commons.apache.org
MultiValueMap class
Este problema se puede resolver con una lista de List<Map.Entry<K,V>>
mapa de entradas. No necesitamos usar ni bibliotecas externas ni nueva aplicación de Mapa. Una entrada de mapa se puede crear de esta manera:
Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);
Aprender de mis errores ... por favor no aplicar esto por su cuenta. multimap guayaba es el camino a seguir.
Una mejora común requerida en Multimapas es no permitir pares duplicados teclas de valor.
La implementación / cambiar esto en un su aplicación puede ser molesto.
En la guayaba su tan simple como:
HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();
ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
Yo tenía una variante ligeramente diferente de este tema: Fue necesario asociar dos valores diferentes con la misma clave. La simple publicación de aquí en caso de que ayuda a los demás, he introducido un HashMap como el valor:
/* @param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
@param innerMap: Key -> String (extIP), Value -> String
If the key exists, retrieve the stored HashMap innerMap
and put the constructed key, value pair
*/
if (frameTypeHash.containsKey(frameID)){
//Key exists, add the key/value to innerHashMap
HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
} else {
HashMap<String, String> innerMap = new HashMap<String, String>();
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
// This means the key doesn't exists, adding it for the first time
frameTypeHash.put(frameID, innerMap );
}
}
En el código anterior el frameID clave se lee de primera cadena de un archivo de entrada en cada línea, el valor para frameTypeHash se construye mediante la división de la línea restante y se almacena como objeto String originalmente, durante un período de tiempo que el archivo comenzó a tener múltiples líneas (con diferentes valores) asociado con misma clave frameID, por lo frameTypeHash se sobrescribe con última línea como el valor. Substituí el objeto String con otro objeto HashMap como el campo de valor, esto ayudó en el mantenimiento de una sola tecla a diferente asignación de valores.
class DuplicateMap<K, V>
{
enum MapType
{
Hash,LinkedHash
}
int HashCode = 0;
Map<Key<K>,V> map = null;
DuplicateMap()
{
map = new HashMap<Key<K>,V>();
}
DuplicateMap( MapType maptype )
{
if ( maptype == MapType.Hash ) {
map = new HashMap<Key<K>,V>();
}
else if ( maptype == MapType.LinkedHash ) {
map = new LinkedHashMap<Key<K>,V>();
}
else
map = new HashMap<Key<K>,V>();
}
V put( K key, V value )
{
return map.put( new Key<K>( key , HashCode++ ), value );
}
void putAll( Map<K, V> map1 )
{
Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();
for ( Entry<K, V> entry : map1.entrySet() ) {
map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
}
map.putAll(map2);
}
Set<Entry<K, V>> entrySet()
{
Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
entry.add( new Entry<K, V>(){
private K Key = entry1.getKey().Key();
private V Value = entry1.getValue();
@Override
public K getKey() {
return Key;
}
@Override
public V getValue() {
return Value;
}
@Override
public V setValue(V value) {
return null;
}});
}
return entry;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{");
boolean FirstIteration = true;
for ( Entry<K, V> entry : entrySet() ) {
builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() ) );
FirstIteration = false;
}
builder.append("}");
return builder.toString();
}
class Key<K1>
{
K1 Key;
int HashCode;
public Key(K1 key, int hashCode) {
super();
Key = key;
HashCode = hashCode;
}
public K1 Key() {
return Key;
}
@Override
public String toString() {
return Key.toString() ;
}
@Override
public int hashCode() {
return HashCode;
}
}
Podría también explicar el contexto para el que usted está tratando de implementar un mapa con duplicados de las llaves? Estoy seguro de que podría haber una solución mejor. Los mapas están destinados a mantener las claves únicas por una buena razón. Aunque si realmente quería hacerlo; siempre se puede extender la clase escribir un simple clase de mapa personalizado que tiene una función de amortiguación de choques y que le permitirá mantener múltiples entradas con las mismas teclas.
Nota: Debe implementar la función de amortiguación de choques de tal forma que, las llaves que colisionan se convierten en conjunto único "siempre". Algo tan simple como, llave con código hash objeto o algo anexar?
sólo para estar completa, Apache Commons colecciones también tiene un MultiMap . La desventaja, por supuesto, es que Apache Commons no utiliza medicamentos genéricos.
Con un poco truco que puede utilizar HashSet con duplicados de las llaves. ADVERTENCIA: esta es la implementación en gran medida dependiente de HashSet.
class MultiKeyPair {
Object key;
Object value;
public MultiKeyPair(Object key, Object value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return key.hashCode();
}
}
class MultiKeyList extends MultiKeyPair {
ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();
public MultiKeyList(Object key) {
super(key, null);
}
@Override
public boolean equals(Object obj) {
list.add((MultiKeyPair) obj);
return false;
}
}
public static void main(String[] args) {
HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
set.add(new MultiKeyPair("A","a1"));
set.add(new MultiKeyPair("A","a2"));
set.add(new MultiKeyPair("B","b1"));
set.add(new MultiKeyPair("A","a3"));
MultiKeyList o = new MultiKeyList("A");
set.contains(o);
for (MultiKeyPair pair : o.list) {
System.out.println(pair.value);
}
}
Si hay duplicados de las llaves luego una tecla puede corresponder a más de un valor. La solución obvia es asignar la clave para una lista de estos valores.
Por ejemplo en Python:
map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"] # It shows john and mike
print map["driver"][0] # It shows john
print map["driver"][1] # It shows mike
He utilizado este:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
1, Map<String, List<String>> map = new HashMap<>();
esta solución verbosa tiene varios inconvenientes y es propenso a errores. Eso implica que necesitamos para crear instancias de una colección para cada valor, la verificación de su presencia antes de añadir o eliminar un valor, eliminar de forma manual cuando no hay Los valores se dejan, etcétera.
2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface
¿qué pasa con un impl tales MultiMap?
public class MultiMap<K, V> extends HashMap<K, Set<V>> {
private static final long serialVersionUID = 1L;
private Map<K, Set<V>> innerMap = new HashMap<>();
public Set<V> put(K key, V value) {
Set<V> valuesOld = this.innerMap.get(key);
HashSet<V> valuesNewTotal = new HashSet<>();
if (valuesOld != null) {
valuesNewTotal.addAll(valuesOld);
}
valuesNewTotal.add(value);
this.innerMap.put(key, valuesNewTotal);
return valuesOld;
}
public void putAll(K key, Set<V> values) {
for (V value : values) {
put(key, value);
}
}
@Override
public Set<V> put(K key, Set<V> value) {
Set<V> valuesOld = this.innerMap.get(key);
putAll(key, value);
return valuesOld;
}
@Override
public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
K key = valueEntry.getKey();
Set<V> value = valueEntry.getValue();
putAll(key, value);
}
}
@Override
public Set<V> putIfAbsent(K key, Set<V> value) {
Set<V> valueOld = this.innerMap.get(key);
if (valueOld == null) {
putAll(key, value);
}
return valueOld;
}
@Override
public Set<V> get(Object key) {
return this.innerMap.get(key);
}
@Override
etc. etc. override all public methods size(), clear() .....
}