How to implement a Map with multiple keys?
Two maps. One Map<K1, V>
and one Map<K2, V>
. If you must have a single interface, write a wrapper class that implements said methods.
Map with multiple keys
Use a TreeMap instead, this way you can make use a custom comparator for your CustomKey class instead of a Hashcode.
TreeMap<CustomKey, List<value>> map = new TreeMap<CustomKey, List<value>>(myComparator);
eta: instead of creating a comparator class you can make the CustomKey class implement Comparable
Java Map with multiple keys
Just store the value twice:
Map<Object, Value> map = new HashMap<>();
map.put(key1, someValue);
map.put(key2, someValue);
The thing is, it doesn't really matter what type the key is, so use a generic bound that allows both key types - Object
is fine.
Note that the parameter type of Map#get()
method is just Object
anyway, so from a look-up perspective there's no value in having separate maps (the type of the key is only relevant for put()
).
How link multiple keys to the same value in the HashMap
If you want to associate a group of keys with the same object, it can be achieved by using a mutable object as a value.
For instance, you can make use of StringBuilder
or implement a custom class. It'll be more performant and easier than an approach with implementing your own map which extends HashMap
and is able to track these groups of keys and triggers a series of updates for each call of put()
, replace()
or remove()
.
Solution with a custom mutable Container
can look like this:
HashMap<String, Container<Integer>> map = new HashMap<>();
Container<Integer> commonValue = new Container<>(0);
map.put("x", commonValue);
map.put("y", commonValue);
System.out.println("Value for 'x': " + map.get("x"));
System.out.println("Value for 'y': " + map.get("y"));
commonValue.setValue(10);
System.out.println("Value for 'x': " + map.get("x"));
System.out.println("Value for 'y': " + map.get("y"));
The Container
class itself.
public class Container<T> {
private T value;
public Container(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
As I have already said, the alternative is to use a mutable class that is already provided by the JDK. The code is then almost the same:
HashMap<String, StringBuilder> map = new HashMap<>();
StringBuilder commonValue = new StringBuilder("0");
map.put("x", commonValue);
map.put("y", commonValue);
System.out.println("Value for 'x': " + map.get("x"));
System.out.println("Value for 'y': " + map.get("y"));
commonValue.replace(0, commonValue.length(), "10");
System.out.println("Value for 'x': " + map.get("x"));
System.out.println("Value for 'y': " + map.get("y"));
Output (by both versions)
Value for 'x': 0
Value for 'y': 0
Value for 'x': 10
Value for 'y': 10
How to create a HashMap with two keys (Key-Pair, Value)?
There are several options:
2 dimensions
Map of maps
Map<Integer, Map<Integer, V>> map = //...
//...
map.get(2).get(5);
Wrapper key object
public class Key {
private final int x;
private final int y;
public Key(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return x == key.x && y == key.y;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
Implementing equals()
and hashCode()
is crucial here. Then you simply use:
Map<Key, V> map = //...
and:
map.get(new Key(2, 5));
Table
from Guava
Table<Integer, Integer, V> table = HashBasedTable.create();
//...
table.get(2, 5);
Table
uses map of maps underneath.
N dimensions
Notice that special Key
class is the only approach that scales to n-dimensions. You might also consider:
Map<List<Integer>, V> map = //...
but that's terrible from performance perspective, as well as readability and correctness (no easy way to enforce list size).
Maybe take a look at Scala where you have tuples and case
classes (replacing whole Key
class with one-liner).
Map with multiple keys mapped to a single value in Java?
I would create a new class and have these 3 keys as attributes such as
class CompositeKey {
private String key1;
private Integer key2;
private Double key3;
public CompositeKey(String key1, Integer key2, Double key3) {
this.key1 = key1;
this.key2 = key2;
this.key3 = key3;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key1 == null) ? 0 : key1.hashCode());
result = prime * result + ((key2 == null) ? 0 : key2.hashCode());
result = prime * result + ((key3 == null) ? 0 : key3.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompositeKey other = (CompositeKey) obj;
if (key1 == null) {
if (other.key1 != null)
return false;
} else if (!key1.equals(other.key1))
return false;
if (key2 == null) {
if (other.key2 != null)
return false;
} else if (!key2.equals(other.key2))
return false;
if (key3 == null) {
if (other.key3 != null)
return false;
} else if (!key3.equals(other.key3))
return false;
return true;
}
public String getKey1() {
return key1;
}
public Integer getKey2() {
return key2;
}
public Double getKey3() {
return key3;
}
}
Then I'll create 3 index maps for each key as follow
// 3 index maps
// List<CompositeKey> not just CompositeKey because one key say k1 can be in combination of 2 different set of k2 and k3.
Map<String, List<CompositeKey>> key1Index = new HashMap<>();
Map<Integer, List<CompositeKey>> key2Index = new HashMap<>();
Map<Double, List<CompositeKey>> key3Index = new HashMap<>();
Then the data map where actual data will be kept
Map<CompositeKey, Object> dataMap = new HashMap<>();
Now, say you want to add key1
,key2
,key3
against Object
. Create CompositeKey
object as key
CompositeKey compositeKey = new CompositeKey(key1, key2, key3);
// Change value accordingly below
dataMap.put(compositeKey, new Object());
Also, update the index maps so that you can use them to look up later
key1Index.computeIfAbsent(key1, k -> new ArrayList<>()).add(compositeKey);
key2Index.computeIfAbsent(key2, k -> new ArrayList<>()).add(compositeKey);
key3Index.computeIfAbsent(key3, k -> new ArrayList<>()).add(compositeKey);
Finally, you can now,
// Search by single key, say key1
List<CompositeKey> list = key1Index.get(key1);
List<Object> result = getResult(list, dataMap);
// Search by two keys, say key 1 and key 2
List<CompositeKey> key1IndexResult = key1Index.get(key1);
List<CompositeKey> key1Key2List = key1IndexResult.stream()
.filter(ck -> ck.getKey2().equals(key2))
.collect(Collectors.toList());
List<Object> key1Key2Result = getResult(key1Key2List, dataMap);
// Search by all 3 keys
CompositeKey allKeys = new CompositeKey(key1, key2, key3);
List<Object> allKeysResult = getResult(Collections.singletonList(allKeys), dataMap);
Utility method used:
private List<Object> getResult(List<CompositeKey> list, Map<CompositeKey, Object> dataMap) {
return list.stream().map(dataMap::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
Working code online link: https://onlinegdb.com/rk2kszLMN
Multiple Keys to Single Value Map Java
The Table data structure in Guava seems to meet you requirement of refering to a value by a pair of objects.
How can I implement a fast map having multiple keys?
Constant look up requires a hash map. You can use a the boost::unordered_map (or tr1). The key would be the combined hash of the int and the void pointer.
HashMap with multiple values under the same key
You could:
- Use a map that has a list as the value.
Map<KeyType, List<ValueType>>
. - Create a new wrapper class and place instances of this wrapper in the map.
Map<KeyType, WrapperType>
. - Use a tuple like class (saves creating lots of wrappers).
Map<KeyType, Tuple<Value1Type, Value2Type>>
. - 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.
Related Topics
Using Heapdumponoutofmemoryerror Parameter for Heap Dump for Jboss
How to Use an Internet Time Server to Get the Time
What Is the Use of Interface Constants
When to Use Comparable and Comparator
In Java How to Sort One List Based on Another
How to Automatically Generate N "Distinct" Colors
Very Confused by Java 8 Comparator Type Inference
Proper Implementation of Cubic Spline Interpolation
Use Mockito/Powermockito to Mock a Public Method of Super Class
Dealing with "Xerces Hell" in Java/Maven
Java Replacing Multiple Different Substring in a String at Once (Or in the Most Efficient Way)
Effective Gif/Image Color Quantization
Convert Integer into Byte Array (Java)
What Does Swingutilities.Invokelater Do
How to Add Directory to Classpath in an Application Run Profile in Intellij Idea