I have a list of objects. The objects are given an ID and stored in a Hashtable. If I need an object with particular ID, I simply say:
ht.get(ID);
However, sometimes I need to get the ID for a given object:
ht.get(Object);
My first idea is to use two different HashTables; one for ID -> Object mapping and the other for Object -> ID mapping.
Does this sound like a good enough solution?
If you cannot use external collections (as you seem to not want to use given one of your comments) you could write a simple class to do what you want (which, yes, is essentially your first thought), along the lines of (I didn't compile this, and it is just a first thought so could be a bad idea, etc ...):
EDIT: now there are two versions, one that allows for duplicate values and one that does not. The ones that does not will remove the key if the value is overwritten.
This version does not allow duplicate values:
class Foo<K, V>
{
private final Map<K, V> keyValue;
private final Map<V, K> valueKey;
{
keyValue = new HashMap<K, V>();
valueKey = new HashMap<V, K>();
}
// this makes sure that if you do not have duplicate values.
public void put(final K key, final V value)
{
if(keyValue.containsValue(value))
{
keyValue.remove(valueKey.get(value));
}
keyValue.put(key, value);
valueKey.put(value, key);
}
public V getValueForKey(final K key)
{
return (keyValue.get(key));
}
public K getKeyForValue(final V value)
{
return (valueKey.get(value));
}
public static void main(final String[] argv)
{
Foo<String, String> foo;
foo = new Foo<String, String>();
foo.put("a", "Hello");
foo.put("b", "World");
foo.put("c", "Hello");
System.out.println(foo.getValueForKey("a"));
System.out.println(foo.getValueForKey("b"));
System.out.println(foo.getValueForKey("c"));
System.out.println(foo.getKeyForValue("Hello"));
System.out.println(foo.getKeyForValue("World"));
}
}
This version allows duplicated values and gives you back a list of all of the keys that have a given value:
class Foo<K, V>
{
private final Map<K, V> keyValue;
private final Map<V, List<K>> valueKeys;
{
keyValue = new HashMap<K, V>();
valueKeys = new HashMap<V, List<K>>();
}
public void put(final K key, final V value)
{
List<K> values;
keyValue.put(key, value);
values = valueKeys.get(value);
if(values == null)
{
values = new ArrayList<K>();
valueKeys.put(value, values);
}
values.add(key);
}
public V getValueForKey(final K key)
{
return (keyValue.get(key));
}
public List<K> getKeyForValue(final V value)
{
return (valueKeys.get(value));
}
public static void main(final String[] argv)
{
Foo<String, String> foo;
foo = new Foo<String, String>();
foo.put("a", "Hello");
foo.put("b", "World");
foo.put("c", "Hello");
System.out.println(foo.getValueForKey("a"));
System.out.println(foo.getValueForKey("b"));
System.out.println(foo.getValueForKey("c"));
System.out.println(foo.getKeyForValue("Hello"));
System.out.println(foo.getKeyForValue("World"));
}
}
Hiding the two maps in a class is a good idea, because of you find a better way later all you need to do is replace the innards of the class and the rest of your code is left untouched.
If using an external library is OK, you should check BiMap on google collections:
http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/BiMap.html
What you are looking for is a bidirectional map. You can find it in the commons collections in the classes implementing the BidiMap interface or the Google Guava.
What you are looking for is a Bi-directional Map.
Try Apache Collections BidiMap.
http://commons.apache.org/collections/api-3.1/org/apache/commons/collections/BidiMap.html
Not that I know of immediatley but you can build one ... How about having a single collection of your objects and several lookup structures (hashmaps or trees) that don't store the objects themselves (for memory saving reasons) but the index into your single collection? This way you use the appropriate lookup structure you need (Id -> object or vice versa) get back an integer value that you can index into your original collection. This way you can do more than a bidirectional lookup in case you need to do so in the future.
Related
I have a cache class in which I used 2 HashMaps to keep the cache.
I want to be able to choose the right map given key's class type so that:
if key is Long, then get value from map longKeyCache
if key is String, then get value from map stringKeyCache.
(assume user will only pass in Long or String key)
For this purpose, I build function getMapToUse.
The problem is that I'll have to declear its return type as Map without any type restrictions. As only so will the function compile correctly and will I be able to use the returned map to store cache in follow up codes (mapToUse.put(key, value)).
The code works but I got warning from IDE - Raw use of parameterized class 'Map'
Would there be a way to tackle this warning? Thanks in advance.
public class CacheManager {
private final Map<Long,String> longKeyCache = new WeakHashMap<>();
private final Map<String,Integer> stringKeyCache = new WeakHashMap<>();
public <K, V> V getCache(K key, Function<K, V> valueLoader) {
Map<K, V> mapToUse = getMapToUse(key);
return Optional.ofNullable(mapToUse.get(key))
// cache miss
.orElseGet(() -> {
V value = valueLoader.apply(key);
mapToUse.put(key, value);
return value;
});
}
// warning: Raw use of parameterized class 'Map'
private <K> Map getMapToUse(K key) {
if (key instanceof Long) {
return longKeyCache;
} else {
return stringKeyCache;
}
}
}
If you want to retain this design (of having a CacheManager that "knows" of two maps that have keys and values of different types), then somewhere or the other you will have to cast the map into Map<K, V>. There is no escaping this because the compiler will not be able to guess which actual type is meant.
You will be aware that one of the purposes of generics is to allow implementing identical functionality that can use various types, but apply type-safety check at the same time.
The method getCache( K, Function<K, V> ) is returning V, which means that V could be either Integer or String. But which one?? The compiler must boil down to one at the place the method is being called.
So, here is how I would implement this: Because there is a small and known set of map key/value types involved, generics may not be the way. Instead I would go by simply using overloaded methods. Reason: The caller still has a consistent API that is compile-time type-checked, which is the main aim with generics.
public class CacheManager{
private final Map<Long,String> longKeyCache = new WeakHashMap<>();
private final Map<String,Integer> stringKeyCache = new WeakHashMap<>();
public String getCache( Long key, Function<Long, String> valueLoader) {
return Optional.ofNullable( longKeyCache.get(key) )
// cache miss
.orElseGet(() -> {
String value = valueLoader.apply(key);
longKeyCache.put(key, value);
return value;
});
}
public Integer getCache( String key, Function<String, Integer> valueLoader) {
return Optional.ofNullable( stringKeyCache.get(key) )
// cache miss
.orElseGet(() -> {
Integer value = valueLoader.apply(key);
stringKeyCache.put(key, value);
return value;
});
}
/* Testing code from here on. */
public static void main( String[] args ){
CacheManager cm = new CacheManager();
cm.addTestData();
System.out.println( cm.getCache( 200L, k -> k.toString() ) );
System.out.println( cm.getCache( "500S", k -> 800 ) );
System.out.println( cm.getCache( "900", k -> 900 ) );
}
private void addTestData() {
longKeyCache.put( 200L, "200S" );
longKeyCache.put( 201L, "201S" );
stringKeyCache.put( "500S", 500 );
stringKeyCache.put( "501S", 501 );
}
}
I am seaching for a data structure that is almost exactly a HashMap<String,Integer>, but the problem with HashMaps is that most of the data stored in key value pairs is lost by calling the putAll() method on two HashMaps, due to the replacement behavior of putVal() in line 655 of the java/util/HashMap.java.
This is basically the change that I want:
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
-- e.value = value;
++ e.value = value + oldValue;
afterNodeAccess(e);
return oldValue;
}
Is there an existing data structure, that I've overlooked that would do such a thing, or how do I create a class that is basically a HashMap with that one change?
I've already tried to code something, but doen't work how I want it to... In fact it doen't matter if I set the put method on #Override, do it like that, or delete it completely - the replacing behavior ofcourse stays the same, because putAll() uses putVal() that I can't reach / change from the outside - or I at least don't know how...
/**
* doesn't work, putAll() uses putVal() that I can't reach
*/
public class SumHashMap<K> extends HashMap<K, Integer> {
private static final long serialVersionUID = 1L;
public Integer put(K key, Integer value) {
Integer oldValue = get(key);
if (oldValue == null)
return super.put(key, value);
return super.put(key, oldValue + value);
}
}
Thanks in advance
Additional info:
I want to use the putAll() function in the reduction of a stream out of custom HashMaps.
If I have two custom HashMaps of this sort {"key1" : 2, "key3" : 4} and {"key3" : 1} the result of a.putAll(b) should be {"key1" : 2, "key3" : 5}
You don't need a new data structure for this, you don't even need a new class that inherits from HashMap. Instead, use the Map.merge method:
newMap.forEach((k, v) -> oldMap.merge(k, v, Integer::sum));
This code uses Map.forEach to traverse the entries of the new map (the one you'd receive as an argument in putAll) and uses Map.merge (along with Integer::sum) to merge its entries into an already existing map (which I've named oldMap here).
I think this is what you are looking for. I made it so that the key can be any type. If you want, you can remove the generic for the key and just extend HashMap<String, Integer>.
import java.util.HashMap;
import java.util.Map;
public class AddingHashMap<K> extends HashMap<K, Integer> {
#Override
public Integer put(K key, Integer value) {
Integer existingValue = super.get(key);
if (existingValue == null) {
existingValue = value;
} else {
existingValue = existingValue.intValue() + value.intValue();
}
return super.put(key, existingValue);
}
#Override
public void putAll(Map<? extends K, ? extends Integer> m) {
m.entrySet().forEach(entry -> {
this.put(entry.getKey(), entry.getValue());
});
}
}
Here is it working:
public static void main(String[] argv) {
AddingHashMap<String> myAddingHashMap = new AddingHashMap<>();
myAddingHashMap.put("One", 1);
myAddingHashMap.put("Two", 2);
myAddingHashMap.put("One", 3);
myAddingHashMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " - " + entry.getValue()));
}
Outputs:
One - 4
Two - 2
Later edit: Keep in mind that this is NOT thread-safe.
I don't think there is a datastructure that does that. The purpose of the datastructure is to store data, not to have logic associated to it. The HashMap can store key-value pairs for you but if you need some more advanced, or specific, logic associated with certain operations, you'll need to add it yourself.
One way is to wrap the map in a class which has this logic. Another might be to implenent the Map interface yourself (which could also use a HashMap internally) though I would not recommend that since changing the behaviour is not a great idea.
A minimal wrapper providing adding functionality:
public class AddingMap {
private final HashMap<String, Integer> map;
public AddingMap() {
map = new HashMap<>();
}
public void add(String key, Integer value) {
map.put(key, map.getOrDefault(key, 0) + value);
}
public Integer get(String key) {
return map.get(key);
}
}
Edit
Shouldn't have finished writing the answer half way...
Indeed, the addAll() method is missing:
public void addAll(Map<String, Integer> map) {
map.entrySet().forEach(e -> this.add(e.getKey(), e.getValue()));
}
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");
}
}
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.
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