I have a question about hashmaps with multiple keys to value. Let's say I have (key / value )
1/a, 1/b, 1/3, 2/aa, 2/bb, 2/cc.
Would this work?
If it does, could I have a way to loop through it and display all values for only either key 1 or 2?
You can use a map with lists as values, e.g.:
HashMap<Integer, List<String>> myMap = new HashMap<Integer, List<String>>();
java.util.HashMap does not allow you to map multiple values to a single key. You want to use one of Guava's Multimap's. Read through the interface to determine which implemented version is suitable for you.
A simple MultiMap would look something like this skeleton:
public class MultiMap<K,V>
{
private Map<K,List<V>> map = new HashMap<K,List<V>>();
public MultiMap()
{
// Define constructors
}
public void put(K key, V value)
{
List<V> list = map.get(key);
if (list == null)
{
list = new ArrayList<V>();
map.put(key, list);
}
list.add(value);
}
public List<V> get(K key)
{
return map.get(key);
}
public int getCount(K key)
{
return map.containsKey(key) ? map.get(key).size() : 0;
}
}
It cannot directly implement Map<K,V> because put can't return the replaced element (you never replace). A full elaboration would define an interface MultiMap<K,V> and an implementation class, I've omitted that for brevity, as well as other methods you might want, such as V remove(K key) and V get(K key, int index)... and anything else you can think of that might be useful :-)
Maps will handle multiple keys to one value since only the keys need be unique:
Map(key, value)
However one key to multiple values requires s multimap of a map strict of :
Map(key, list(values))
Also, whatever you use as a key really should implement a good hadhCode() function if you decide to use a HashMap and/or HashSet
Edit: had to use() instead of <> because my mobile or sof's mobile site editor clobbered the <> symbols....odd
Related
How can I check if there is a value using the fields of a given value? And put new one?
In ConcurrentHashMap, cause I have N threads.
Here is an example of what I want. However, it is not thread-safe.
Map<Integer, Record> map = new ConcurrentHashMap<>();
// it works, but I think it's unsafe
int get(Object key) {
for (Map.Entry<Integer, Record> next : map.entrySet()) {
if (next.getValue().a == key) {
return next.getValue().b;
}
}
int code = ...newCode();
map.put(code, new Record(...))
return code;
}
record Record(Object a, int b) {
}
What you're suggesting would defeat the purpose of using a HashMap since you're iterating through the Map instead of retrieving from the Map.
What you should really do is create a new Map where the field in Record.a is the Key and the field in Record.B is the value (or just the whole Record). Then just update your logic to insert into both Maps appropriately.
I am using drools workbench and I have trouble setting a hashmap. I want to avoid adding a method just to add values to the map so I am trying to find a workaround.
When I need to set the value of a list I use:
setList(Arrays.asList("string one", "string two", ...));
I was wondering if such a method exists for hashmaps.
If you are allowed to use 3rd party libs you can use Guava's ImmutableMap
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
If you are going to use Map, then definitely you should store the data in from of (Key, Value) pair. Now , in HashMap, you have two methods to store data,
1. put(Object any) - This method takes single object.
2. putAll(Map otherMap) - This method takes some other map, and will add all the elements of that map to yours one.
So If these methods are not suitable for you, then I think you should write your own method to add values. May be you can write as below.
class DroolMap<K,V> extends HashMap<K,V> {
public DroolMap() {
super();
}
public DroolMap(int size) {
super(size);
}
public DroolMap<K, V> add(K key, V value) {
this.put(key, value);
return this;
}
}
class TestDroolMap {
public void testDroolMap() {
DroolMap<String, String> droolMap = new DroolMap<String, String>();
// You can add as many <Key, Value> pairs in one line
droolMap.add("k1", "v1").add("k2", "v2").add("k3", "v3");
}
}
Based on the following code snippet :
Hashtable balance = new Hashtable();
Enumeration names;
String str;
double bal;
balance.put("Zara", new Double(3434.34)); //first entry for Zara
balance.put("Mahnaz", new Double(123.22));
balance.put("Zara", new Double(1378.00)); //second entry for Zara
balance.put("Daisy", new Double(99.22));
balance.put("Qadir", new Double(-19.08));
System.out.println(balance.entrySet());
.
Output : [Qadir=-19.08, Mahnaz=123.22, Daisy=99.22, Zara=1378.0]
Why isn't chaining happening here? When I re-enter with Zara as key the old value is overwritten. I expected it to be added at the end of the Linked List at Zara".hashcode() index.
Does Java use separate chaining only for collision handling?
If I can't use chaining( as I'v tried above) please suggest a common method to do so.
Does Java use separate chaining only for collision handling?
Yes. You can only have one entry per key in a Hashtable (or HashMap, which is what you should probably be using - along with generics). It's a key/value map, not a key/multiple-values map. In the context of a hash table, the term "collision" is usually used for the situation where two unequal keys have the same hash code. They still need to be treated as different keys, so the implementation has to cope with that. That's not the situation you're in.
It sounds like you might want a multi-map, such as one of the ones in Guava. You can then ask a multimap for all values associated with a particular key.
EDIT: If you want to build your own sort of multimap, you'd have something like:
// Warning: completely untested
public final class Multimap<K, V> {
private final Map<K, List<V>> map = new HashMap<>();
public void add(K key, V value) {
List<V> list = map.get(key);
if (list == null) {
list = new ArrayList();
map.put(key, list);
}
list.add(value);
}
public Iterable<V> getValues(K key) {
List<V> list = map.get(key);
return list == null ? Collections.<V>emptyList()
: Collections.unmodifiableList(list);
}
}
Quote from the documentation of Map (which Hashtable is an implementation of):
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
(emphasis mine)
The documentation of put() also says:
If the map previously contained a mapping for the key, the old value is replaced by the specified value
So if you want multiple values associated with a key, use a Map<String, List<Double>> instead of a Map<String, Double>. Guava also has a Multimap, which does what you want without having to deal with Lists explicitely as with a Map<String, List<Double>>.
I'm trying to create an extension to HashMap that modifies the put function. The goal is to have the put function not allow duplicate values. So, if someone tried to insert the key/value (B,a) when the key/value (A,a) was already in the map, then it would replace the key /value (A,a) with (B,a) rather than create a new key/value pair.
The problem I'm having is that I don't know how to find a key that corresponds with a value without iterating the table (which I can't do since table is a private variable and I can't access it from within my function extension).
I have also tried to retrieve the set of keys with keySet() with the intention of running get() operations on every single key, but the set variable is really confusing me and I'm not sure how to correctly iterate and run get() operations on the individual elements.
EDIT:
Here is what I have so far:
import java.util.*;
public class UniqueHashMap extends HashMap {
public V put(K key, V value) {
boolean contains = containsValue(value);
if (contains == true)
// INSERT CODE HERE
else
return super.put(key, value);
}
}
In the if statement, what I want to do is be able to:
1) Locate the key corresponding to "value".
2) Delete the key.
3) Insert the new key/value pair.
The part I'm having trouble with is 1) because most of the solutions I've seen need access to the map (which I don't know how to get since it's a private variable in HashMap). I can get access to the keys using keySet(), but I don't know how to iteratively run get() operations on the individual keys because I am confused about the set variable.
One way to do it:
import java.util.HashMap;
import java.util.Map.Entry;
public class MyHashMap<K, V> {
private HashMap<K, V> map;
void put(K key, V value) {
if (map.containsValue(value)) {
K keyToRemove = findKeyByValue(value);
map.remove(keyToRemove);
map.put(key, value);
} else {
map.put(key, value);
}
}
private K findKeyByValue(V val) {
for (Entry<K, V> e : map.entrySet()) {
if (val == e.getValue())
return e.getKey();
}
return null;
}
}
But note that this way you will lose HashMap's constant complexity of put method.
EDIT: Included a compilable class
I think what you are looking for is the entrySet() method of Map. This allows you to iterate over the pairs of key and corresponding value together:
public void put(K key, V val) {
Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
while (entries.hasNext()) {
if (entries.next().getValue().equals(val)) {
entries.remove();
break;
}
}
super.put(key, val);
}
You could extend HashMap (might be better to implement the Map yourself and use a HashMap to implement it incase they add more add methods), and override the add methods and on each one:
If HashMap contains the key already
grab the instance that the key refers to and remove it from the map, and re-put that instance into the map with the supplied key instead of the old key
Else
Just put with the supplied key and value.
Keep track of what value was last associated with the key with an internal HashSet, this is so you can do an O(1) remove for that If portion above.
Use Google Guava's BiMap. It was designed to handle this case as it is a bi-directional map.
You would call Object key = myBiMap.inverse().get( myValue );.
Need a dynamic data structure, which may be similar to a MAP(Java.util.Map), wchich is capable of storing, String and Object. And that Object may again need to store another map, which can store, String and Object.
I suspect that the requester is looking for something like the below:
class MultilevelMap<K,V> extends HashMap<List<K>,V> {
#SafeVarargs
public final put(V value, K keys...) {
put(makeKey(keys), value);
}
#SafeVarargs
public final V get(K keys...) {
return get(makeKey(keys));
}
// The remainder of this class is left as a tedious exercise for the reader
private List<K> makeKey(K[] keys) {
List<K> key = new ArrayList<K>(keys.size);
for(K k: keys) {
key.add(k);
}
return key;
}
}
A Trie, as far as I understand it is similar, but opposite. It presents an interface of Map<S,V>, but internally is implemented as a variable-depth Map<K,Map<K, ... V>> where K are consecutive affixes of S, such that if you concatenate all of the Ks between the top of the tree and V, you get the S you used as the key. The above presents an interface of (very approximately) Map<K,K, ... , V>, but internally is Map<List<K>, V>.
You can nest maps (and other containers) to arbitrary depth. This puts a Map in another Map in another ... to a total depth of 10:
private Map<String, Object> nest(int levelsLeft, Map<String, Object> parent) {
if (levelsLeft > 0) {
parent.put("key" + levelsLeft,
nest(levelsLeft - 1, new HashMap<String, Object>()));
}
return parent;
}
// from somewhere else
Map<String, Object> nested = nest(10, new Map<String, Object>());
((Map<String, Object>)nested.get("key10")).get("key9"); // goes all the way down to "key1"
Note that the price for declaring a Map<String, Object> is that, whenever you access something via get(), you need to cast it to whatever it actually is to be able to use it as something more specific than an Object.
Sounds like you either need a Multimap or a Trie.