HashMap avec plusieurs valeurs sous la même clé

199

Est-il possible pour nous d'implémenter un HashMap avec une clé et deux valeurs. Tout comme HashMap?

S'il vous plaît, aidez-moi, en disant également (s'il n'y a aucun moyen) une autre façon d'implémenter le stockage de trois valeurs avec une comme clé?

vidhya
la source
2
doublon possible de Comment stocker plus d'une chaîne dans une carte?
Joachim Sauer
Merci mes amis ... mais j'ai quelques limitations dans l'utilisation de MultiHashMap
vidhya
Copie possible de l' implémentation
Steve Chambers

Réponses:

266

Vous pourriez:

  1. Utilisez une carte qui a une liste comme valeur. Map<KeyType, List<ValueType>>.
  2. Créez une nouvelle classe wrapper et placez des instances de ce wrapper dans la carte. Map<KeyType, WrapperType>.
  3. Utilisez une classe de type tuple (économise la création de nombreux wrappers). Map<KeyType, Tuple<Value1Type, Value2Type>>.
  4. Utilisez plusieurs cartes côte à côte.

Exemples

1. Carte avec liste comme valeur

// 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];

L'inconvénient de cette approche est que la liste n'est pas liée à exactement deux valeurs.

2. Utilisation de la classe 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;

L'inconvénient de cette approche est que vous devez écrire beaucoup de code de plaque chauffante pour toutes ces classes de conteneurs très simples.

3. Utiliser un tuple

// 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;

C'est la meilleure solution à mon avis.

4. Plusieurs cartes

// 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"];

L'inconvénient de cette solution est qu'il n'est pas évident que les deux cartes soient liées, une erreur de programmation pourrait voir les deux cartes se désynchroniser.

Paul Ruane
la source
Salut Paul ... pouvez-vous être un peu plus clair ...? Par un exemple ...?
vidhya
@vidhya: qui correspond en particulier à votre problème? Vos objets multiples sont-ils du même type ou différents?
Paul Ruane
L'exemple serait génial en fait.
Xonatron
@Paul, tout exemple de code simple pour # 3 Map<KeyType, Tuple<Value1Type, Value2Type>>
Joarder Kamal
@CoolMind Je suis sûr que les gens peuvent contourner les erreurs: ou vous pourriez peut-être les corriger?
Paul Ruane
61

Non, pas seulement en tant que HashMap. Vous auriez essentiellement besoin HashMapd'une clé à une collection de valeurs.

Si vous êtes heureux d'utiliser des bibliothèques externes, Guava a exactement ce concept Multimapavec des implémentations telles que ArrayListMultimapet HashMultimap.

Jon Skeet
la source
@ Jon, pourriez-vous fournir un exemple de travail en Java pour la question ci-dessus posée par OP. Très apprécié si vous pouviez le poster
Deepak
2
@Deepak: Recherchez des exemples multimap de goyave et vous trouverez un exemple de code.
Jon Skeet
1
@Deepak: Fondamentalement, vous construisez quelque chose comme ArrayListMultimapvous ... ou utilisez simplement un HashMap<String, List<Integer>>ou autre chose. Vous devez créer une liste vide chaque fois qu'une valeur est ajoutée pour la première fois, en gros.
Jon Skeet
1
avez-vous un exemple de travail pourHashMap<String, List<Integer>>
Deepak
9
@Deepak: Je vous suggère d'essayer de créer un exemple vous-même, et si vous êtes bloqué, posez une question incluant le code autant que vous en avez. Vous en apprendrez beaucoup plus de cette façon.
Jon Skeet
23

Un autre bon choix est d'utiliser MultiValuedMap d'Apache Commons. Jetez un œil aux classes d'implémentation connues en haut de la page pour les implémentations spécialisées.

Exemple:

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

pourrait être remplacé par

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

Alors,

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

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

entraînerait une collection collcontenant "A", "B" et "C".

Matthew Steven Monkan
la source
13

Jetez un œil aux Multimapbibliothèques de goyaves et à leur mise en œuvre -HashMultimap

Une collection similaire à une carte, mais qui peut associer plusieurs valeurs à une seule clé. Si vous appelez put (K, V) deux fois, avec la même clé mais des valeurs différentes, le multimap contient des mappages de la clé aux deux valeurs.

Bozho
la source
7

J'utilise Map<KeyType, Object[]>pour associer plusieurs valeurs à une clé dans une carte. De cette façon, je peux stocker plusieurs valeurs de différents types associées à une clé. Vous devez faire attention en maintenant un ordre correct d'insertion et de récupération à partir de l'objet [].

Exemple: Considérez, nous voulons stocker les informations des étudiants. La clé est l'identifiant, alors que nous aimerions stocker le nom, l'adresse et l'e-mail associés à l'étudiant.

       //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
la source
1
Pour moi, c'est la meilleure réponse. C'est plus simple, plus concis et moins abstrait.
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
la source
4

Juste pour mémoire, la solution pure JDK8 serait d'utiliser la Map::computeméthode:

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

Tel que

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);
}

avec sortie:

bar: foo
first: hello, foo, hello

Notez que pour garantir la cohérence dans le cas où plusieurs threads accèdent à cette structure de données ConcurrentHashMapet CopyOnWriteArrayListdoivent par exemple être utilisés.

Stepan Vavra
la source
Il vaut mieux l'utiliser computeIfAbsent. map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
saka1029
3

Si vous utilisez Spring Framework . Il y a: org.springframework.util.MultiValueMap.

Pour créer une carte multi-valeurs non modifiable:

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

Ou utiliser org.springframework.util.LinkedMultiValueMap

Igor Rybak
la source
2

Oui et non. La solution consiste à créer un clas Wrapper pour vos valeurs qui contient les 2 (3 ou plus) valeurs qui correspondent à votre clé.

Nicolas
la source
2

Le moyen le plus simple serait d'utiliser une bibliothèque de collections 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
    }

}

lien maven: https://mvnrepository.com/artifact/com.google.collections/google-collections/1.0-rc2

plus à ce sujet: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html

Jorciney
la source
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);
}

J'ai les meilleurs résultats.

Il vous suffit de créer de nouveaux HashMapcomme

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

dans votre forboucle. Il aura le même effet que la même clé et plusieurs valeurs.

Shahid Ahmad
la source
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"));
   }                                                                                            
 }                                                                    

Production:

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

      ["111"]

Il s'agit de la bibliothèque Google-Guava pour les fonctionnalités utilitaires. C'est la solution requise.

Ank_247shbm
la source
C'est une solution valable et j'ai utilisé cette approche à plusieurs reprises.
letowianka
oui ça marche mais ça montre des données au format [] je veux ces éléments un par un comment faire pour que les pls soient coincés ici
Sunil Chaudhary
0

Je n'ai pas pu poster de réponse sur le commentaire de Paul, je crée donc un nouveau commentaire pour Vidhya ici:

Wrapper sera un SuperClasspour les deux classes que nous voulons stocker en tant que valeur.

et à l'intérieur de la classe wrapper, nous pouvons mettre les associations comme objets variables d'instance pour les deux objets de classe.

par exemple

class MyWrapper {

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

et dans HashMap nous pouvons mettre de cette façon,

Map<KeyObject, WrapperObject> 

WrapperObj aura des variables de classe:class1Obj, class2Obj

Dhruva Mistry
la source
0

Vous pouvez le faire implicitement.

// 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";

C'est très simple et efficace. Si vous souhaitez plutôt des valeurs de différentes classes, vous pouvez effectuer les opérations suivantes:

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

Peut être fait en utilisant une identité HashMap, soumise à la condition que la comparaison des clés soit effectuée par l'opérateur == et non égal ().

jayendra bhatt
la source
0

Je préfère ce qui suit pour stocker un certain nombre de variables sans avoir à créer une classe distincte.

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

Je suis tellement habitué à le faire avec un dictionnaire de données dans l'objectif C. Il était plus difficile d'obtenir un résultat similaire dans Java pour Android. J'ai fini par créer une classe personnalisée, puis en faisant simplement une table de hachage de ma classe personnalisée.

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
la source
0

Utilisation de Java Collectors

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

où le département est votre clé

0cnLaroche
la source
-9

Essayez LinkedHashMap , exemple:

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

production:

clés: 1,1,2,3

valeurs: liées, hachage, carte, java

d.gjinovci
la source
7
Ça ne marchera pas. linkedn'existera plus sur la carte car vous l'avez remplacée par hash.
Jeff Mercado