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.
Related
I'm looking at the structure of LinkedCaseInsensitiveMap (spring framework 5.0.5.RELEASE). I'm curious why LinkedCaseInsensitiveMap uses both LinkedHashMap and HashMap, and why not just use LinkedHashMap like this?
private final LinkedHashMap<String, V> targetMap;
public V get(Object key) {
if (key instanceof String) {
return this.targetMap.get(convertKey((String) key));
}
return null;
}
private final LinkedHashMap<String, V> targetMap;
private final HashMap<String, String> caseInsensitiveKeys;
In this case targetMap contains real-case string to your object, and caseInsensitiveKeys contains mapping your key in lower case to your real-case key.
It allows to show you real-case keys when you are doing for-each iteration, but at the same time it allows you to have case insensitivity.
So let say, following code:
LinkedCaseInsensitiveMap<Object> map = new LinkedCaseInsensitiveMap<>();
map.put("MyCustomObject", new Object());
will put "MyCustomObject" -> new Object() in targetMap, and "mycustomobject" -> "MyCustomObject" in caseInsensitiveKeys. And now if you try to print all objects from your map it will print it as you added and not changed keys. You can't archive it without second map.
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");
}
}
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
In Java, I am implementing this:
List<Entry<String, Integer>> listObjects = new ArrayList<Entry<String, Integer>>();
but how can I add a new Entry?
as it does not work with: listObjects.add(new Entry<"abc", 1>());
thanks in advance.
I know it's a pretty older thread but you can do it as follows:
listObjects.add(new java.util.AbstractMap.SimpleEntry<String, Integer>("abc", 1));
It might help someone like me, who was trying to do this recently!
I hope it helps :-)
Do you mean Map.Entry? That is an interface (so you can't instantiate without an implementation class, you can learn about interfaces in the Java Tutorial). Entry instances are usually only created by Map implementations and only exposed through Map.entrySet()
Of course, since it's an interface you could add your own implementation, something like this:
public class MyEntry<K, V> implements Entry<K, V> {
private final K key;
private V value;
public MyEntry(final K key) {
this.key = key;
}
public MyEntry(final K key, final V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(final V value) {
final V oldValue = this.value;
this.value = value;
return oldValue;
}
}
That way you could do listObjects.add(new MyEntry<String,Integer>("abc", 1))
But that doesn't really make sense outside of a map context.
Entry is a parametized class and you need to create instances of Entry with a constructor (typical way).
Without knowing the implementation of Entry: this could already work (at least it shows how it usually works):
// create a list
List<Entry<String, Integer>> listObjects =
new ArrayList<Entry<String, Integer>>()
// create an instance of Entry
Entry<String, Integer> entry = new Entry<String, Integer>("abc", 1);
// add the instance of Entry to the list
listObjects.add(entry);
With Map.Entry it is somewhat different (OP just mentioned, that Entry is Map.Entry in Fact. We can't create Map.Entry instances, we usually get them from an existing map:
Map<String, Integer> map = getMapFromSomewhere();
List<Map.Entry<String, Integer>> listObjects =
new ArrayList<Map.Entry<String, Integer>>();
for (Map.Entry<String, Integer> entry:map.entrySet())
listObjects.add(entry);
One way to use a ready made implementation of Entry would be Guava's Immutable Entry
Something like this listObjects.add(Maps.immutableEntry("abc",1));
Perhaps listObjects.add(new Entry<String, Integer>("abc", 1));? The generics specify the type (String, Integer), not the values (abc, 1).
Edit - Note that it was later added that the Entry is in fact Map.Entry, so you need to create an implementation of Map.Entry that you later instantiate, exactly as the chosen answer explains.
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.