HashMap con múltiples valores bajo la misma clave

199

¿Es posible para nosotros implementar un HashMap con una clave y dos valores? Justo como HashMap?

Por favor, ayúdenme, también diciendo (si no hay forma) alguna otra forma de implementar el almacenamiento de tres valores con uno como clave.

vidhya
fuente
2
posible duplicado de ¿Cómo almacenar más de una cadena en un Mapa?
Joachim Sauer
Gracias amigos ... pero tengo algunas limitaciones al usar MultiHashMap
vidhya
Posible duplicado de la implementación
Steve Chambers

Respuestas:

266

Tú podrías:

  1. Use un mapa que tenga una lista como valor. Map<KeyType, List<ValueType>>.
  2. Cree una nueva clase de contenedor y coloque instancias de este contenedor en el mapa. Map<KeyType, WrapperType>.
  3. Use una clase de tupla (ahorra creando muchos envoltorios). Map<KeyType, Tuple<Value1Type, Value2Type>>.
  4. Use mapas múltiples de lado a lado.

Ejemplos

1. Mapa con la lista como valor

// create our map
Map<String, List<Person>> peopleByForename = new HashMap<>();    

// populate it
List<Person> people = new ArrayList<>();
people.add(new Person("Bob Smith"));
people.add(new Person("Bob Jones"));
peopleByForename.put("Bob", people);

// read from it
List<Person> bobs = peopleByForename["Bob"];
Person bob1 = bobs[0];
Person bob2 = bobs[1];

La desventaja de este enfoque es que la lista no está vinculada exactamente a dos valores.

2. Usando la clase wrapper

// define our wrapper
class Wrapper {
    public Wrapper(Person person1, Person person2) {
       this.person1 = person1;
       this.person2 = person2;
    }

    public Person getPerson1 { return this.person1; }
    public Person getPerson2 { return this.person2; }

    private Person person1;
    private Person person2;
}

// create our map
Map<String, Wrapper> peopleByForename = new HashMap<>();

// populate it
Wrapper people = new Wrapper();
peopleByForename.put("Bob", new Wrapper(new Person("Bob Smith"),
                                        new Person("Bob Jones"));

// read from it
Wrapper bobs = peopleByForename.get("Bob");
Person bob1 = bobs.getPerson1;
Person bob2 = bobs.getPerson2;

La desventaja de este enfoque es que debe escribir mucho código de placa de caldera para todas estas clases de contenedores muy simples.

3. Usando una tupla

// you'll have to write or download a Tuple class in Java, (.NET ships with one)

// create our map
Map<String, Tuple2<Person, Person> peopleByForename = new HashMap<>();

// populate it
peopleByForename.put("Bob", new Tuple2(new Person("Bob Smith",
                                       new Person("Bob Jones"));

// read from it
Tuple<Person, Person> bobs = peopleByForename["Bob"];
Person bob1 = bobs.Item1;
Person bob2 = bobs.Item2;

Esta es la mejor solución en mi opinión.

4. Múltiples mapas

// create our maps
Map<String, Person> firstPersonByForename = new HashMap<>();
Map<String, Person> secondPersonByForename = new HashMap<>();

// populate them
firstPersonByForename.put("Bob", new Person("Bob Smith"));
secondPersonByForename.put("Bob", new Person("Bob Jones"));

// read from them
Person bob1 = firstPersonByForename["Bob"];
Person bob2 = secondPersonByForename["Bob"];

La desventaja de esta solución es que no es obvio que los dos mapas estén relacionados, un error programático podría hacer que los dos mapas no estén sincronizados.

Paul Ruane
fuente
Hola Paul ... ¿puedes dejarlo un poco más claro ... por un ejemplo ...?
vidhya
@vidhya: ¿cuál se adapta particularmente a tu problema? ¿Sus objetos múltiples son del mismo tipo o diferentes?
Paul Ruane
El ejemplo sería genial en realidad.
Xonatron
@Paul, cualquier código de ejemplo simple para # 3 Map<KeyType, Tuple<Value1Type, Value2Type>>
Joarder Kamal
@CoolMind Estoy seguro de que las personas pueden sortear los errores: ¿o quizás podría corregirlos?
Paul Ruane
61

No, no solo como un HashMap. Básicamente, necesitaría una HashMapclave a una colección de valores.

Si está contento de usar bibliotecas externas, Guava tiene exactamente este concepto Multimapcon implementaciones como ArrayListMultimapy HashMultimap.

Jon Skeet
fuente
@ Jon, ¿podría proporcionar un ejemplo de trabajo en Java para la pregunta anterior formulada por OP? Le agradezco mucho si pudiera publicarlo
Deepak
2
@Deepak: busque ejemplos de múltiples mapas de guayaba y encontrará un código de muestra.
Jon Skeet
1
@Deepak: Básicamente construirías algo como ArrayListMultimaptú ... o simplemente usarías una HashMap<String, List<Integer>>o lo que sea. Tendría que crear una lista vacía cada vez que se agrega un valor por primera vez, básicamente.
Jon Skeet
1
¿tiene un ejemplo de trabajo paraHashMap<String, List<Integer>>
Deepak
9
@Deepak: le sugiero que intente crear un ejemplo usted mismo y, si se atasca, haga una pregunta que incluya el código hasta donde tenga. Aprenderás mucho más de esa manera.
Jon Skeet
23

Otra buena opción es usar MultiValuedMap de Apache Commons. Eche un vistazo a Todas las clases de implementación conocidas en la parte superior de la página para implementaciones especializadas.

Ejemplo:

HashMap<K, ArrayList<String>> map = new HashMap<K, ArrayList<String>>()

podría ser reemplazado con

MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();

Entonces,

map.put(key, "A");
map.put(key, "B");
map.put(key, "C");

Collection<String> coll = map.get(key);

daría como resultado una colección que collcontiene "A", "B" y "C".

Matthew Steven Monkan
fuente
13

Eche un vistazo a Multimaplas bibliotecas de guayaba y su implementación:HashMultimap

Una colección similar a un Mapa, pero que puede asociar múltiples valores con una sola clave. Si llama a put (K, V) dos veces, con la misma clave pero con valores diferentes, el mapa múltiple contiene asignaciones de la clave a ambos valores.

Bozho
fuente
7

Lo uso Map<KeyType, Object[]>para asociar múltiples valores con una clave en un mapa. De esta manera, puedo almacenar múltiples valores de diferentes tipos asociados con una clave. Debe tener cuidado manteniendo el orden adecuado de inserción y recuperación desde el Objeto [].

Ejemplo: considere, queremos almacenar información del estudiante. La clave es id, mientras que nos gustaría almacenar el nombre, la dirección y el correo electrónico asociados al estudiante.

       //To make entry into Map
        Map<Integer, String[]> studenMap = new HashMap<Integer, String[]>();
        String[] studentInformationArray = new String[]{"name", "address", "email"};
        int studenId = 1;
        studenMap.put(studenId, studentInformationArray);

        //To retrieve values from Map
        String name = studenMap.get(studenId)[1];
        String address = studenMap.get(studenId)[2];
        String email = studenMap.get(studenId)[3];
Sudarshan_SMD
fuente
1
Para mí esta es la mejor respuesta. Es más simple, más conciso y menos abstracto.
Morey
6
HashMap<Integer,ArrayList<String>> map = new    HashMap<Integer,ArrayList<String>>();

ArrayList<String> list = new ArrayList<String>();
list.add("abc");
list.add("xyz");
map.put(100,list);
Janarthanan Ramu
fuente
4

Solo para el registro, la solución JDK8 pura sería utilizar el Map::computemétodo:

map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);

Como

public static void main(String[] args) {
    Map<String, List<String>> map = new HashMap<>();

    put(map, "first", "hello");
    put(map, "first", "foo");
    put(map, "bar", "foo");
    put(map, "first", "hello");

    map.forEach((s, strings) -> {
        System.out.print(s + ": ");
        System.out.println(strings.stream().collect(Collectors.joining(", ")));
    });
}

private static <KEY, VALUE> void put(Map<KEY, List<VALUE>> map, KEY key, VALUE value) {
    map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);
}

con salida:

bar: foo
first: hello, foo, hello

Tenga en cuenta que para garantizar la coherencia en caso de múltiples subprocesos tienen acceso a esta estructura de datos, ConcurrentHashMapy CopyOnWriteArrayList, por ejemplo, necesidad de ser utilizado.

Stepan Vavra
fuente
Es mejor usar computeIfAbsent. map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
saka1029
3

Si usa Spring Framework . No es: org.springframework.util.MultiValueMap.

Para crear un mapa de valores múltiples no modificable:

Map<String,List<String>> map = ...
MultiValueMap<String, String> multiValueMap = CollectionUtils.toMultiValueMap(map);

O usar org.springframework.util.LinkedMultiValueMap

Igor Rybak
fuente
2

Si y no. La solución es crear un clasificador Wrapper para sus valores que contenga los 2 (3 o más) valores que corresponden a su clave.

Nicolas
fuente
2

La forma más fácil sería usar una biblioteca de colección de Google:

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

public class Test {

    public static void main(final String[] args) {

        // multimap can handle one key with a list of values
        final Multimap<String, String> cars = ArrayListMultimap.create();
        cars.put("Nissan", "Qashqai");
        cars.put("Nissan", "Juke");
        cars.put("Bmw", "M3");
        cars.put("Bmw", "330E");
        cars.put("Bmw", "X6");
        cars.put("Bmw", "X5");

        cars.get("Bmw").forEach(System.out::println);

        // It will print the:
        // M3
        // 330E
        // X6
        // X5
    }

}

Enlace Maven: https://mvnrepository.com/artifact/com.google.collections/google-collections/1.0-rc2

más sobre esto: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html

Jorciney
fuente
1
String key= "services_servicename"

ArrayList<String> data;

for(int i = 0; i lessthen data.size(); i++) {
    HashMap<String, String> servicesNameHashmap = new HashMap<String, String>();
    servicesNameHashmap.put(key,data.get(i).getServiceName());
    mServiceNameArray.add(i,servicesNameHashmap);
}

Tengo los mejores resultados.

Solo tienes que crear nuevos me HashMapgusta

HashMap<String, String> servicesNameHashmap = new HashMap<String, String>();

en tu forbucle Tendrá el mismo efecto que la misma clave y múltiples valores.

Shahid Ahmad
fuente
1
 import java.io.*;
 import java.util.*;

 import com.google.common.collect.*;

 class finTech{
public static void main(String args[]){
       Multimap<String, String> multimap = ArrayListMultimap.create();
       multimap.put("1","11");
       multimap.put("1","14");
       multimap.put("1","12");
       multimap.put("1","13");
       multimap.put("11","111");
       multimap.put("12","121");
        System.out.println(multimap);
        System.out.println(multimap.get("11"));
   }                                                                                            
 }                                                                    

Salida:

     {"1"=["11","12","13","14"],"11"=["111"],"12"=["121"]}

      ["111"]

Esta es la biblioteca Google-Guava para funcionalidades de utilidad. Esta es la solución requerida.

Ank_247shbm
fuente
Es una solución válida y he usado este enfoque en varias ocasiones.
Letowianka
sí, funciona, pero muestra datos en formato []. Quiero esos elementos uno por uno cómo conseguir que los pls me queden atrapados aquí
Sunil Chaudhary
0

No pude publicar una respuesta al comentario de Paul, así que estoy creando un nuevo comentario para Vidhya aquí:

Wrapper será un SuperClasspara las dos clases que queremos almacenar como valor.

y dentro de la clase wrapper, podemos poner las asociaciones como objetos de variable de instancia para los dos objetos de clase.

p.ej

class MyWrapper {

 Class1 class1obj = new Class1();
 Class2 class2obj = new Class2();
...
}

y en HashMap podemos poner de esta manera,

Map<KeyObject, WrapperObject> 

WrapperObj tendrá variables de clase:class1Obj, class2Obj

Miseria Dhruva
fuente
0

Puedes hacerlo implícitamente.

// Create the map. There is no restriction to the size that the array String can have
HashMap<Integer, String[]> map = new HashMap<Integer, String[]>();

//initialize a key chosing the array of String you want for your values
map.put(1, new String[] { "name1", "name2" });

//edit value of a key
map.get(1)[0] = "othername";

Esto es muy simple y efectivo. Si desea valores de diferentes clases, puede hacer lo siguiente:

HashMap<Integer, Object[]> map = new HashMap<Integer, Object[]>();
Veiga
fuente
0

Se puede hacer usando un identityHashMap, sujeto a la condición de que la comparación de claves será realizada por == operador y no es igual a ().

jayendra bhatt
fuente
0

Prefiero lo siguiente para almacenar cualquier número de variables sin tener que crear una clase separada.

final public static Map<String, Map<String, Float>> myMap    = new HashMap<String, Map<String, Float>>();
ozgeneral
fuente
0

Estoy tan acostumbrado a hacer esto con un Diccionario de datos en el Objetivo C. Fue más difícil obtener un resultado similar en Java para Android. Terminé creando una clase personalizada y luego haciendo un hashmap de mi clase personalizada.

public class Test1 {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.addview);

//create the datastring
    HashMap<Integer, myClass> hm = new HashMap<Integer, myClass>();
    hm.put(1, new myClass("Car", "Small", 3000));
    hm.put(2, new myClass("Truck", "Large", 4000));
    hm.put(3, new myClass("Motorcycle", "Small", 1000));

//pull the datastring back for a specific item.
//also can edit the data using the set methods.  this just shows getting it for display.
    myClass test1 = hm.get(1);
    String testitem = test1.getItem();
    int testprice = test1.getPrice();
    Log.i("Class Info Example",testitem+Integer.toString(testprice));
}
}

//custom class.  You could make it public to use on several activities, or just include in the activity if using only here
class myClass{
    private String item;
    private String type;
    private int price;

    public myClass(String itm, String ty, int pr){
        this.item = itm;
        this.price = pr;
        this.type = ty;
    }

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

    public String getType() {
        return item;
    }

    public void setType(String type) {
        this.type = type;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

}
Vette
fuente
0

Uso de recopiladores de Java

// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
                    .collect(Collectors.groupingBy(Employee::getDepartment));

donde departamento es tu llave

0cnLaroche
fuente
-9

Pruebe LinkedHashMap , muestra:

Map<String,String> map = new LinkedHashMap<String,String>();    
map.put('1','linked');map.put('1','hash');    
map.put('2','map');map.put('3','java');.. 

salida:

claves: 1,1,2,3

valores: vinculado, hash, mapa, java

d.gjinovci
fuente
77
Eso no funcionará. linkedya no existirá en el mapa porque lo reemplazaste por hash.
Jeff Mercado