Bidirectional Map - java

Can you suggest a kind of map or similar data structure where we can get both the value and key from each other at equal ease. That is to say, that each may be used to find other.

Java doesn't have a bidirectional map in its standard library.
Use for example BiMap<K, V> from Google Guava .

If you feel it pain importing some third party library.
How about this simple class.
public class BiMap<K,V> {
HashMap<K,V> map = new HashMap<K, V>();
HashMap<V,K> inversedMap = new HashMap<V, K>();
void put(K k, V v) {
map.put(k, v);
inversedMap.put(v, k);
}
V get(K k) {
return map.get(k);
}
K getKey(V v) {
return inversedMap.get(v);
}
}
Make sure K and V class has proper hashCode implementation.

The most common solution is using two maps. You can easily encapsulate them in a class with a friendly interface by extending AbstractMap. (Update: This is how Guava's HashBiMap is implemented: two maps)
Creating a new data structure using nothing but arrays and custom classes has few advantages. The map implementations are lightweight wrappers of a data structure that indexes the keys. Since you need two indexes you might as well use two complete maps.

Also try Apache Commons Collections 4 BidiMap Package.

Google Guava contains a BiMap (BiDirectional Map).

well for the average usecase where you need a Dictionary like that, I see nothing wrong with a KISS solution, just put'ting the key and value vice versa, saving the overhead of a second Map or even library only for that purpose:
myMap.put("apple", "Apfel");
myMap.put("Apfel", "apple");

Based on this answer in this QA and its comments I wrote following. [Will be tested]
Bidirectional Map
import java.util.HashMap;
public class BidirectionalMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
public HashMap<V, K> inversedMap = new HashMap<V, K>();
public K getKey(V value) {
return inversedMap.get(value);
}
#Override
public int size() {
return this.size();
}
#Override
public boolean isEmpty() {
return this.size() > 0;
}
#Override
public V remove(Object key) {
V val=super.remove(key);
inversedMap.remove(val);
return val;
}
#Override
public V get(Object key) {
return super.get(key);
}
#Override
public V put(K key, V value) {
inversedMap.put(value, key);
return super.put(key, value);
}
}

You can define an enum and define helper method to get key. Performance is way too far better compared to BidiMap.
E.g
public enum Fruit {
APPLE("_apple");
private final String value;
Fruit(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
public static String getKey(String value){
Fruit fruits[] = Fruit.values();
for(Fruit fruit : fruits){
if(value.equals(fruit.value)){
return fruit.name();
}
}
return null; }
}

Based on this tutorial I suggest the following as answer:
public class IdToNames {
public static void main(String[] args){
BidiMap<String, Integer> map = new DualHashBidiMap<>();
map.put("NameA", 100);
map.put("NameB", 200);
System.out.println(map.size()); //2 as expected
System.out.println(map.get("NameA")); //100 as expected
System.out.println(map.getKey(100)); //"NameA" as expected
}
}
Note the problem of duplicated keys and/or values described in this question here

Related

Is Java generics required in this Dictionary code?

In the below code, interface Dictionary has some methods using Object type as parameter.
/* Dictionary.java */
package cs61b.homework6.dict;
public interface Dictionary {
public int size();
public boolean isEmpty();
class Entry {
protected Object key;
protected Object value;
public Object key() {
return key;
}
public Object value() {
return value;
}
}
public Entry insert(Object key, Object value);
public Entry find(Object key);
public Entry remove(Object key);
public void makeEmpty();
}
below is the implementation class HashTableChained of interface Dictionary,
/* HashTableChained.java */
package cs61b.homework6.dict;
import java.util.ArrayList;
import java.util.Iterator;
import JavaCollections.list.DblyLinkList;
public class HashTableChained implements Dictionary {
private long tableSize;
private ArrayList<DblyLinkList<Entry>> defTable;
public HashTableChained(long sizeEstimate) {... }
public HashTableChained() { ... }
private static boolean isPrime(long n) { ...}
private static long nextPrime(long previous) { .. }
int compFunction(int code) { ... }
public int size() { ... }
public boolean isEmpty() { ... }
public Entry insert(Object key, Object value) { ... }
public Entry find(Object key) { ... }
public Entry remove(Object key) { ... }
public void makeEmpty() { ...}
}
I would like to understand, Is there an advantage of introducing interface Dictionary<K, V> syntax with K key and V value?
Note: Java beginner. Complete code is available here. Teacher encourages to write own packages instead of using java.util collection package.
There is an advantage. It will keep you safe(r) during compilation by verifying you're not doing anything completely wrong (like putting a key or a value of the wrong type).
It will also remove (most of) the need to cast in your code when using the map.
if you use Entry with Objects as keys and values for doing a word count:
Dictionary dict = new Dictionary();
dict.insert("word", new Integer(42));
Object count = dict.find("word"); // gives an Object, not an Integer
// need to cast - annoying, not safe
Integer countAsInteger = (Integer)count;
If you introduce generics:
Dictionary dict = new Dictionary<String, Integer>();
dict.insert("word", new Integer(42));
Integer count = dict.find("word"); // gives an Integer
The generic typing also protects you from creating a heterogeneous map. In you implementation this is allowed:
dict.insert("word", "42");
But it was probably an error. You intended the count to be an Integer.
In the generic implementation you will be able to implement:
public void insert(K key, V value);
Which will not allow (at compilation time) anything other than K and V in the map.

Extend HashMap to return empty HashSet for non-found keys

I want to make a HashMap which contains HashSets as values and returns an empty HashSet when the key is not found.
public class IsbnHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public IsbnHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
#Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
However my implementation does not work.
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>();
This returns "HashSet cannot be applied". If I try to change K,V in IsbnHashMap to <String, HashSet<String>> I get some funky errors as well. How can I implement this?
First it should be noted that in Java-8 you can use instead:
isbnToId.computeIfAbsent(isbn, k -> new HashSet<>()).add(_id);
Second, if you really want to do something like this in previous Java versions, you'd better to create separate method for this purpose (for example, getOrDefault()) in order not to violate the contract. Third, you need to create new HashSet<>() for every new key. If you return the same instance, it will be shared between given keys. If you don't expect users to modify it, it's better to use unmodifiable Collections.emptySet() as default value. This way users may safely do isbnToId.getOrDefault(isbn).contains(_id), but trying isbnToId.getOrDefault(isbn).add(_id) will result in exception. If you want to support the modification (prior to Java-8), you can, for example, pass the element class to the constructor instead:
public static class MyMap<K, V> extends HashMap<K, V> {
private Class<?> clazz;
public MyMap(Class<?> clazz) {
this.clazz = clazz;
}
public V getOrCompute(K key) {
V v = get(key);
if(v == null) {
try {
v = (V) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
put(key, v);
}
return v;
}
}
Usage example:
MyMap<String, Set<String>> map = new MyMap<>(HashSet.class);
map.getOrCompute("a").add("b");
map.getOrCompute("a").add("c");
map.getOrCompute("d").add("e");
System.out.println(map); // {a=[b, c], d=[e]}
Here we assume that instantiating the passed class with default constructor is ok. An alternative would be to pass the factory interface which is capable to produce the default values.
As Jon Skeet said ...
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>(new HashSet<String>());
... however, that would return the same default object as Dunni pointed out.
So this will do:
private static HashMap<String, HashSet<String>> isbnToId = new HashMap<String, HashSet<String>>();
public static void coupleIsbnToId(String isbn, String _id) {
if (!isbnToId.containsKey(isbn)) {
isbnToId.put(isbn, new HashSet<String>());
}
isbnToId.get(isbn).add(_id);
}

Qt-like multivalue map for Java?

Is there an existing open source Map implementation for java, which would be a normal key-value map, but would also support multiple values per key? The multimap implementations I've found seem to associate key with collection, which doesn't quite cut it, as I need a drop-in replacement for existing code.
I sense some people saying "you can't do that", so here's an example of one way how it can behave, in a widely used framework, Qt. Here's an excerpt form the docs for QMap class:
If the map contains no item with key key, the function returns a
default-constructed value. If there are multiple items for key in the
map, the value of the most recently inserted one is returned.
My need is quite limited, so at the moment I'm using the hack below, which is adequate, since there are no removals and many values per key are exception, and the duplicate keys getting a bit mangled is not a problem:
public static <V, V2 extends V> String mapMultiPut(
Map<String, V> map,
String key,
V2 value) {
int count = 0;
String tmpKey = key;
while (map.containsKey(tmpKey)) {
++count;
tmpKey = key + '_' + count;
}
map.put(tmpKey, value);
return tmpKey;
}
But I'd like a nicer solution, if one exists...
You could use a ListMultimap along with
Iterables.getLast(listMultiMap.get(key), defaultValue(key))
where you define your own defaultValue method.
This assumes you don't actually need the Map interface in your class.
If you really want a Map you could try this
public abstract class QtMap<K, V> extends ForwardingMap<K, V>
{
private final ListMultimap<K, V> listMultimap = ArrayListMultimap.create();
final Map<K, V> delegate = Maps.<K, Collection<V>, V> transformEntries(listMultimap.asMap(), new EntryTransformer<K, Collection<V>, V>()
{
#Override
public V transformEntry(K key, Collection<V> value)
{
return Iterables.getLast(value, defaultValue(key));
}
});
#Override
protected Map<K, V> delegate()
{
return delegate;
}
#Override
public V put(K key, V value)
{
listMultimap.put(key, value);
return null;
}
#Override
public void putAll(Map<? extends K, ? extends V> map)
{
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet())
{
put(entry.getKey(), entry.getValue());
}
}
#Override
public V get(Object key)
{
return listMultimap.containsKey(key) ? delegate.get(key) : defaultValue(key);
}
protected abstract V defaultValue(Object key);
}
although it's only sketchily tested
Guava libraries have a Multimap which allows more than one value per key :)

Is there a basic id / value object in Java?

I've created an "Attribut" class which is just a wrapper for a key/value single item. I know that Maps and HashMaps are designed for lists of this kind of items so I feel like i reinvented the wheel...
Is there some Class which fulfill this purpose ?
Regards
( My code to be clear about what i'm looking for )
public class Attribut {
private int id;
private String value;
#Override
public String toString() {
return value;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
You can reuse Map.Entry<K, V>:
http://docs.oracle.com/javase/6/docs/api/java/util/Map.Entry.html
In your case it'd be Map.Entry<Integer, String>.
HashMap !
example :
Map<Integer,String> attribut = new HashMap<Integer, String>();
attribut.put(1, "hi");
String value = attribut.get(1);
you can iterate :
for (Integer key : attribut.keySet()) {
value = attribut.get(key);
}
EDIT :
OK, just for a Pair !
public class Pair<K, V> {
private final K element0;
private final V element1;
public static <K, V> Pair<K, V> createPair(K key, V value) {
return new Pair<K, V>(key, value);
}
public Pair(K element0, V element1) {
this.element0 = element0;
this.element1 = element1;
}
public K getElement0() {
return element0;
}
public V getElement1() {
return element1;
}
}
usage :
Pair<Integer, String> pair = Pair.createPair(1, "test");
pair.getElement0();
pair.getElement1();
Immutable, only a pair !
You can use AbstractMap.SimpleEntry. There is also a SimpleImmutableEntry.
However, I believe that it is not wrong designing your own type. There is a plethora of examples in the JDK itself where something like this (tuple) has been done:
java.awt.Dimension
java.awt.Point
I believe that it's a good thing, since you're code is more easily readable and you gain additional type safety.
You're not "reinventing the wheel", you just specifying your requirements. You want a class that constitutes a mutable int/String pair, and so your code is OK.
Your problem is that Java syntax is overly verbose. It would be nice to simply define it as something like
class IdValuePair(id: int, value: String)
but that's something for other languages.
You could use [Collections.singletonMap()](http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#singletonMap(K, V)).

is there a case insensitive multimap in google collections

I need a multi map which keys are case insensitive. is there such implementation in google collections?
Here is a case insensitive version of a ForwardingMap:
public class CaseInsensitiveForwardingMap<V> extends ForwardingMap<String, V>
implements Serializable{
private static final long serialVersionUID = -7741335486707072323L;
// default constructor
public CaseInsensitiveForwardingMap(){
this(new HashMap<String, V>());
}
// constructor with a supplied map
public CaseInsensitiveForwardingMap(final Map<String, V> inner){
this.inner = inner;
}
private final Map<String, V> inner;
#Override
protected Map<String, V> delegate(){
return inner;
}
// convert keys to lower case Strings, preserve null keys
private static String lower(final Object key){
return key == null ? null : key.toString().toLowerCase();
}
#Override
public V get(final Object key){ return inner.get(lower(key)); }
#Override
public void putAll(final Map<? extends String, ? extends V> map){
if(map == null || map.isEmpty()){ inner.putAll(map); }
else{
for(final Entry<? extends String, ? extends V> entry :
map.entrySet()){
inner.put(lower(entry.getKey()), entry.getValue());
}
}
}
#Override
public V remove(final Object object){ return inner.remove(lower(object)); }
#Override
public boolean containsKey(final Object key){
return inner.containsKey(lower(key));
}
#Override
public V put(final String key, final V value){
return inner.put(lower(key), value);
}
}
Using this map, you can create the MultiMap using the Supplier methods in MultiMaps.
Example:
Map<String, Collection<String>> map =
new CaseInsensitiveForwardingMap<Collection<String>>();
Multimap<String, String> caseInsensitiveMultiMap =
Multimaps.newMultimap(map, new Supplier<Collection<String>>(){
#Override
public Collection<String> get(){ return Sets.newHashSet(); }
});
Caveat: keySet() will return lowercase values only, regardless how the keys were entered.
Couldn't you use a Map<String,List<Payload>> and give it a Comparator<String> which did a case-insensitive compare?
It appears that neither Google Collections nor Apache Collection frameworks have a multimap that accepts a Comparator for evaluating key equality.
You could define a case-insensitive String Comparator using a Collator. Then create a TreeMultimap with keys sorted by that Comparator.
No, but presumably you're using String keys? If so, why not just normalise all access to a regular multimap? For the 80% case, that'll be making all calls puts and gets lowercase the key.
For a full discussion of the issues with case-insensitive multimaps, see this google group discussion

Categories

Resources