HashMap mit mehreren Werten unter demselben Schlüssel

199

Ist es uns möglich, eine HashMap mit einem Schlüssel und zwei Werten zu implementieren? Genau wie HashMap?

Bitte helfen Sie mir auch, indem Sie (wenn es keine Möglichkeit gibt) eine andere Möglichkeit angeben, die Speicherung von drei Werten mit einem als Schlüssel zu implementieren.

Vidhya
quelle
2
Mögliches Duplikat von Wie speichere ich mehr als eine Zeichenfolge in einer Karte?
Joachim Sauer
Danke Freunde ... aber ich habe einige Einschränkungen bei der Verwendung von MultiHashMap
vidhya
Mögliches Duplikat der Map-Implementierung mit doppelten Schlüsseln
Steve Chambers

Antworten:

266

Du könntest:

  1. Verwenden Sie eine Karte mit einer Liste als Wert. Map<KeyType, List<ValueType>>.
  2. Erstellen Sie eine neue Wrapper-Klasse und platzieren Sie Instanzen dieses Wrappers in der Map. Map<KeyType, WrapperType>.
  3. Verwenden Sie eine Tupel-ähnliche Klasse (spart das Erstellen vieler Wrapper). Map<KeyType, Tuple<Value1Type, Value2Type>>.
  4. Verwenden Sie mehrere Karten nebeneinander.

Beispiele

1. Karte mit Liste als Wert

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

Der Nachteil bei diesem Ansatz ist, dass die Liste nicht an genau zwei Werte gebunden ist.

2. Verwenden der Wrapper-Klasse

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

Der Nachteil dieses Ansatzes besteht darin, dass Sie für all diese sehr einfachen Containerklassen viel Kesselplattencode schreiben müssen.

3. Verwenden eines Tupels

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

Dies ist meiner Meinung nach die beste Lösung.

4. Mehrere Karten

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

Der Nachteil dieser Lösung ist, dass es nicht offensichtlich ist, dass die beiden Karten zusammenhängen. Ein Programmfehler könnte dazu führen, dass die beiden Karten nicht mehr synchron sind.

Paul Ruane
quelle
Hallo Paul ... kannst du es ein bisschen klarer machen ...? An einem Beispiel ...?
Vidhya
@vidhya: was passt besonders zu deinem Problem? Sind Ihre mehreren Objekte vom gleichen Typ oder unterschiedlich?
Paul Ruane
Beispiel wäre eigentlich toll.
Xonatron
@ Paul, jeder einfache Beispielcode für # 3 Map<KeyType, Tuple<Value1Type, Value2Type>>
Joarder Kamal
@CoolMind Ich bin sicher, die Leute können sich um die Fehler kümmern: oder könnten Sie sie vielleicht korrigieren?
Paul Ruane
61

Nein, nicht nur als HashMap. Grundsätzlich benötigen Sie einen HashMapSchlüssel von Schlüssel zu einer Sammlung von Werten.

Wenn Sie gerne externe Bibliotheken verwenden, hat Guava genau dieses Konzept in MultimapImplementierungen wie ArrayListMultimapund HashMultimap.

Jon Skeet
quelle
@ Jon, könnten Sie ein Arbeitsbeispiel in Java für die obige Frage von OP zur Verfügung stellen. Sehr geschätzt, wenn Sie es posten könnten
Deepak
2
@Deepak: Suchen Sie nach Guaven-Multimap-Beispielen und Sie finden Beispielcode.
Jon Skeet
1
@Deepak: Grundsätzlich würdest du so etwas wie ArrayListMultimapdich selbst bauen ... oder einfach ein HashMap<String, List<Integer>>oder was auch immer verwenden. Grundsätzlich müssen Sie jedes Mal, wenn ein Wert zum ersten Mal hinzugefügt wird, eine leere Liste erstellen.
Jon Skeet
1
Haben Sie ein funktionierendes Beispiel fürHashMap<String, List<Integer>>
Deepak
9
@Deepak: Ich schlage vor, Sie versuchen, selbst ein Beispiel zu erstellen, und wenn Sie nicht weiterkommen, stellen Sie eine Frage mit dem Code, so weit Sie haben. Auf diese Weise lernen Sie viel mehr.
Jon Skeet
23

Eine weitere gute Wahl ist die Verwendung von MultiValuedMap von Apache Commons. In den All Known Implementing Classes oben auf der Seite finden Sie spezielle Implementierungen.

Beispiel:

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

könnte durch ersetzt werden

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

So,

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

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

würde zu einer Sammlung führen, colldie "A", "B" und "C" enthält.

Matthew Steven Monkan
quelle
13

Schauen Sie sich Multimapdie Guavenbibliotheken und ihre Implementierung an -HashMultimap

Eine Sammlung ähnlich einer Karte, die jedoch einem einzelnen Schlüssel mehrere Werte zuordnen kann. Wenn Sie put (K, V) zweimal mit demselben Schlüssel, aber unterschiedlichen Werten aufrufen, enthält die Multimap Zuordnungen vom Schlüssel zu beiden Werten.

Bozho
quelle
7

Ich verwende Map<KeyType, Object[]>zum Verknüpfen mehrerer Werte mit einem Schlüssel in einer Karte. Auf diese Weise kann ich mehrere Werte verschiedener Typen speichern, die einem Schlüssel zugeordnet sind. Sie müssen darauf achten, dass die Reihenfolge beim Einfügen und Abrufen von Object [] korrekt ist.

Beispiel: Bedenken Sie, wir möchten Schülerinformationen speichern. Der Schlüssel ist die ID, während wir den Namen, die Adresse und die E-Mail-Adresse des Schülers speichern möchten.

       //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
quelle
1
Für mich ist das die beste Antwort. Es ist einfacher, prägnanter und weniger abstrakt.
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
quelle
4

Nur zur Veranschaulichung Map::compute: Die reine JDK8-Lösung wäre die Verwendung der folgenden Methode:

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

Sowie

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

mit Ausgabe:

bar: foo
first: hello, foo, hello

Beachten Sie, dass zur Gewährleistung der Konsistenz für den Fall, dass mehrere Threads auf diese Datenstruktur zugreifen ConcurrentHashMapund CopyOnWriteArrayListbeispielsweise verwendet werden müssen.

Stepan Vavra
quelle
Es ist besser zu benutzen computeIfAbsent. map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
saka1029
3

Wenn Sie Spring Framework verwenden . Es gibt : org.springframework.util.MultiValueMap.

So erstellen Sie eine nicht modifizierbare Mehrwertkarte:

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

Oder verwenden org.springframework.util.LinkedMultiValueMap

Igor Rybak
quelle
2

Ja und nein. Die Lösung besteht darin, eine Wrapper-Klasse für Ihre Werte zu erstellen, die die 2 (3 oder mehr) Werte enthält, die Ihrem Schlüssel entsprechen.

Nicolas
quelle
2

Am einfachsten wäre es, eine Google-Sammlungsbibliothek zu verwenden:

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
    }

}

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

mehr dazu: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html

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

Ich habe die besten Ergebnisse erzielt.

Sie müssen nur neue HashMapwie erstellen

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

in deiner forSchleife. Es hat den gleichen Effekt wie der gleiche Schlüssel und mehrere Werte.

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

Ausgabe:

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

      ["111"]

Dies ist die Google-Guava-Bibliothek für Dienstprogrammfunktionen. Dies ist die erforderliche Lösung.

Ank_247shbm
quelle
Es ist eine gültige Lösung und ich habe diesen Ansatz mehrmals verwendet.
Letowianka
Ja, es funktioniert, aber es zeigt Daten in [] Format. Ich möchte, dass diese Elemente nacheinander angezeigt werden, damit ich bitte hier festsitze
Sunil Chaudhary,
0

Ich konnte keine Antwort auf Pauls Kommentar posten, daher erstelle ich hier einen neuen Kommentar für Vidhya:

Wrapper ist ein SuperClassfür die beiden Klassen, die wir als Wert speichern möchten.

In der Wrapper-Klasse können wir die Zuordnungen als Instanzvariablenobjekte für die beiden Klassenobjekte festlegen.

z.B

class MyWrapper {

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

und in HashMap können wir auf diese Weise setzen,

Map<KeyObject, WrapperObject> 

WrapperObj hat Klassenvariablen:class1Obj, class2Obj

Dhruva Mistry
quelle
0

Sie können es implizit tun.

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

Dies ist sehr einfach und effektiv. Wenn Sie stattdessen Werte verschiedener Klassen möchten, können Sie Folgendes tun:

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

Kann mit einer identityHashMap durchgeführt werden, unter der Bedingung, dass der Schlüsselvergleich vom Operator == und nicht von equals () durchgeführt wird.

Jayendra Bhatt
quelle
0

Ich bevorzuge Folgendes, um eine beliebige Anzahl von Variablen zu speichern, ohne eine separate Klasse erstellen zu müssen.

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

Ich bin es gewohnt, dies nur mit einem Datenwörterbuch in Ziel C zu tun. Es war schwieriger, ein ähnliches Ergebnis in Java für Android zu erzielen. Am Ende habe ich eine benutzerdefinierte Klasse erstellt und dann nur eine Hashmap meiner benutzerdefinierten Klasse erstellt.

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
quelle
0

Verwenden von Java Collectors

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

wo Abteilung ist Ihr Schlüssel

0cnLaroche
quelle
-9

Versuchen Sie LinkedHashMap , Beispiel:

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

Ausgabe:

Schlüssel: 1,1,2,3

Werte: verknüpft, Hash, Map, Java

d.gjinovci
quelle
7
Das wird nicht funktionieren. linkedwird nicht mehr in der Karte vorhanden sein, da Sie sie durch ersetzt haben hash.
Jeff Mercado