245

Is it possible to implement a HashMap with one key and two values?
Just as HashMap<userId, clientID,timeStamp>?

If not, is there any other way to implement the storage of multiple values e.g. one key and two values?

Ola Ström
  • 4,136
  • 5
  • 22
  • 41
vidhya
  • 2,861
  • 5
  • 28
  • 28
  • 2
    possible duplicate of [How to store more than one string in a Map ?](http://stackoverflow.com/questions/3725703/how-to-store-more-than-one-string-in-a-map) – Joachim Sauer Feb 10 '11 at 12:10
  • Thanks friends...but i have some limitations in using MultiHashMap – vidhya Feb 10 '11 at 13:43
  • Possible duplicate of [Map implementation with duplicate keys](http://stackoverflow.com/questions/1062960/map-implementation-with-duplicate-keys) – Steve Chambers Dec 16 '13 at 12:29

21 Answers21

311

You could:

  1. Use a map that has a list as the value. Map<KeyType, List<ValueType>>.
  2. Create a new wrapper class and place instances of this wrapper in the map. Map<KeyType, WrapperType>.
  3. Use a tuple like class (saves creating lots of wrappers). Map<KeyType, Tuple<Value1Type, Value2Type>>.
  4. Use mulitple maps side-by-side.

Examples

1. Map with list as the value

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

The disadvantage with this approach is that the list is not bound to exactly two values.

2. Using wrapper class

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

The disadvantage to this approach is that you have to write a lot of boiler-plate code for all of these very simple container classes.

3. Using a 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;

This is the best solution in my opinion.

4. Multiple maps

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

The disadvantage of this solution is that it's not obvious that the two maps are related, a programmatic error could see the two maps get out of sync.

Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
  • @vidhya: which in particular fits your problem? Are your mulitple objects the same type or different? – Paul Ruane Feb 10 '11 at 14:49
  • @CoolMind I'm sure people can work their way around the mistakes: or you could correct them perhaps? – Paul Ruane Dec 24 '15 at 16:16
  • @PaulRuane, sorry for a criticysm. Yeap, you are right, people can overcome them. – CoolMind Dec 24 '15 at 23:52
  • @PaulRuane Where could I find this tuple class? –  Feb 09 '16 at 16:49
  • @DeliriousSyntax https://dzone.com/articles/whats-wrong-java-8-part-v It took me 10 seconds in a search engine. – Paul Ruane Feb 09 '16 at 17:29
  • @PaulRuane Sorry I found 1 as well https://github.com/sharwell/antlr4/tree/optimized/runtime/Java/src/org/antlr/v4/runtime/misc sorry for wasting your time –  Feb 09 '16 at 17:36
  • If The Map is of Type , you can just append a separator like ":", and then specify the other value(in case you do not want to change the type of Map). – Manoj Majumdar Jul 20 '18 at 07:36
67

No, not just as a HashMap. You'd basically need a HashMap from a key to a collection of values.

If you're happy to use external libraries, Guava has exactly this concept in Multimap with implementations such as ArrayListMultimap, HashMultimap, LinkedHashMultimap etc.

Multimap<String, Integer> nameToNumbers = HashMultimap.create();

System.out.println(nameToNumbers.put("Ann", 5)); // true
System.out.println(nameToNumbers.put("Ann", 5)); // false
nameToNumbers.put("Ann", 6);
nameToNumbers.put("Sam", 7);

System.out.println(nameToNumbers.size()); // 3
System.out.println(nameToNumbers.keySet().size()); // 2
Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • @Jon,could you provide witha working example in Java for the above question asked by OP.Highly appreciated if you could post it – Deepak Feb 10 '11 at 18:04
  • 2
    @Deepak: Search for guava multimap examples and you'll find sample code. – Jon Skeet Feb 10 '11 at 18:11
  • @Jon,sorry to bother you again and again,i dont want to use gauva,how do i achieve it in Core Java.?we dont use Gauava and we not supposed to use MultiMap.could you provide witha working example in Core Java only – Deepak Feb 10 '11 at 18:16
  • 1
    @Deepak: Basically you'd build something like `ArrayListMultimap` yourself... or just use a `HashMap>` or whatever. You'd need to create an empty list any time a value is added for the first time, basically. – Jon Skeet Feb 10 '11 at 18:20
  • 1
    do you have a working example for `HashMap>` – Deepak Feb 10 '11 at 18:26
  • 9
    @Deepak: I suggest you try creating an example yourself, and if you get stuck, ask a question including the code as far as you've got. You'll learn a lot more that way. – Jon Skeet Feb 10 '11 at 19:28
  • 1
    Multimap multimap = ArrayListMultimap.create(); multimap.put("key", "value"); – Bienvenido David May 05 '18 at 14:14
27

Another nice choice is to use MultiValuedMap from Apache Commons. Take a look at the All Known Implementing Classes at the top of the page for specialized implementations.

Example:

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

could be replaced with

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

would result in collection coll containing "A", "B", and "C".

Matthew Steven Monkan
  • 8,170
  • 4
  • 53
  • 71
14

Take a look at Multimap from the guava-libraries and its implementation - HashMultimap

A collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
11

I use Map<KeyType, Object[]> for associating multiple values with a key in a Map. This way, I can store multiple values of different types associated with a key. You have to take care by maintaining proper order of inserting and retrieving from Object[].

Example: Consider, we want to store Student information. Key is id, while we would like to store name, address and email associated to the student.

       //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
  • 2,549
  • 33
  • 23
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
  • 1,331
  • 16
  • 17
6

If you use Spring Framework. There is: org.springframework.util.MultiValueMap.

To create unmodifiable multi value map:

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

Or use org.springframework.util.LinkedMultiValueMap

Ihor Rybak
  • 3,091
  • 2
  • 25
  • 32
5

The easiest way would be to use a google collection library:

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

more on this: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html

LEQADA
  • 1,913
  • 3
  • 22
  • 41
Jorciney
  • 674
  • 10
  • 11
4

Just for the record, the pure JDK8 solution would be to use Map::compute method:

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

Such as

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

with output:

bar: foo
first: hello, foo, hello

Note that to ensure consistency in case multiple threads access this data structure, ConcurrentHashMap and CopyOnWriteArrayList for instance need to be used.

Stepan Vavra
  • 3,884
  • 5
  • 29
  • 40
  • 1
    It is better to use `computeIfAbsent`. `map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);` –  Dec 30 '16 at 00:54
2

Yes and no. The solution is to build a Wrapper clas for your values that contains the 2 (3, or more) values that correspond to your key.

Nicolas
  • 24,509
  • 5
  • 60
  • 66
2

Yes, this is frequently called a multimap.

See: http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/Multimap.html

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
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);
}

I have got the Best Results.

You just have to create new HashMap like

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

in your for loop. It will have same effect like same key and multiple values.

LEQADA
  • 1,913
  • 3
  • 22
  • 41
1

Using Java Collectors

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

where Department is your key

0cnLaroche
  • 268
  • 1
  • 4
  • 8
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"));
   }                                                                                            
 }                                                                    

Output:

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

      ["111"]

This is Google-Guava library for utility functionalities. This is the required solution.

Ank_247shbm
  • 512
  • 7
  • 17
0

I could not post a reply on Paul's comment so I am creating new comment for Vidhya here:

Wrapper will be a SuperClass for the two classes which we want to store as a value.

and inside wrapper class, we can put the associations as the instance variable objects for the two class objects.

e.g.

class MyWrapper {

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

and in HashMap we can put in this way,

Map<KeyObject, WrapperObject> 

WrapperObj will have class variables: class1Obj, class2Obj

Puffin GDI
  • 1,702
  • 5
  • 27
  • 37
0

You can do it implicitly.

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

This is very simple and effective. If you want values of diferent classes instead, you can do the following:

HashMap<Integer, Object[]> map = new HashMap<Integer, Object[]>();
Veiga
  • 212
  • 1
  • 9
0

Can be done using an identityHashMap, subjected to the condition that the keys comparison will be done by == operator and not equals().

jayendra bhatt
  • 1,337
  • 2
  • 19
  • 41
0

I prefer the following to store any number of variables without having to create a separate class.

final public static Map<String, Map<String, Float>> myMap    = new HashMap<String, Map<String, Float>>();
ozgeneral
  • 6,079
  • 2
  • 30
  • 45
0

I am so used to just doing this with a Data Dictionary in Objective C. It was harder to get a similar result in Java for Android. I ended up creating a custom class, and then just doing a hashmap of my custom class.

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
  • 511
  • 5
  • 10
0

We can create a class to have multiple keys or values and the object of this class can be used as a parameter in map. You can refer to https://stackoverflow.com/a/44181931/8065321

metame
  • 2,480
  • 1
  • 17
  • 22
Vikas Pal
  • 11
  • 2
0

Apache Commons collection classes can implement multiple values under same key.

MultiMap multiMapDemo = new MultiValueMap();

multiMapDemo .put("fruit", "Mango");
multiMapDemo .put("fruit", "Orange");
multiMapDemo.put("fruit", "Blueberry");

System.out.println(multiMapDemo.get("fruit"));

Maven Dependency

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-collections4</artifactId>
   <version>4.4</version>
</dependency>
madx
  • 6,723
  • 4
  • 55
  • 59
Vikas Piprade
  • 272
  • 2
  • 7
  • > [`MultiValueMap`](https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/MultiValueMap.html) is deprecated since 4.1. Use `MultiValuedMap` instead – Ilya Serbis May 24 '21 at 20:38