For the purpose of learning I try to make a MultiMap implementation. (and for avoiding relying on other libraries for a library I make).
It doesn't have to be perfect.
At the moment I have this:
class MultiMap<k, v> implements Map {
HashMap<k, List<v>> hMap = new HashMap<k, List<v>>();
public MultiMap () {
}
Followed by all #Override methods from Map.
One is like this:
#Override
public Object get(Object o) {
return hMap.get(o);
}
I have problems with this one:
#Override
public Object put(Object o, Object o2) {
// will return a list
Object toReturn = get(o);
if(hMap.containsValue(o)) {
// is this even possible?
(List<v>)(List<?>)get(o); // <<< problem: "Syntax error on token(s), misplaced construct(s)"
// ^ next .add(o2);
}
// etc.
return toReturn;
}
Is it possible to get a List out of the get method?
You define class like this class MultiMap<k, v> implements Map<k, List<v>>
and then add new method public List<v> put(k key, v value) like that.
public List<v> put(k key, v value) {
List<v> list = get(key);
if(list == null)
list = new ArrayList<>();
list.add(value);
return list;
}
Related
I am trying to extend a solution to the problem posted here.
The castList method shared there works greats for non-generic types.
public static <T> List<T> castList(Object obj, Class<T> clazz)
{
List<T> result = new ArrayList<T>();
if(obj instanceof List<?>)
{
for (Object o : (List<?>) obj)
{
result.add(clazz.cast(o));
}
return result;
}
return null;
}
The issue we are having is trying to use this same idea when the return type is not something simple like a list of strings, but when the list is itself of another generic, like a list of maps.
List<Map<String, Object>> list = (List<Map<String, Object>>) out.get("o_cursor");
Trying to use castList as follows produces type mismatch: cannot convert from List<Map> to List<Map<String, Object>>. Is there a way to extend castList to do this?
List<Map<String, Object>> list = castList(out.get("o_cursor"), Map.class);
I attempted a modified version specific to lists of maps, but still get an error "The method castMapList(Object, Class<Map<K,V>>) is not applicable for the arguments
(Object, Class<Map>)" which seems like a different flavor of the same error.
public static <K, V> List<Map<K, V>> castMapList(Object object, Class<Map<K, V>> clazz) {
if (object instanceof List<?>) {
List <Map<K, V>> result = new ArrayList<Map<K, V>>();
for (Object o: (List<?>) object) {
result.add(clazz.cast(o));
}
return result;
}
return null;
}
Because of erasure, there is:
No way to have a Class<Map<K, V>>.
No built-in way to check at run-time that every particular Map has some particular K and V.
That is:
As you found out, every generic class has a corresponding raw type Class object, e.g. Map.class is a Class<Map>.
Something like obj instanceof Map<K, V> is a compiler error and Class objects do not check generic arguments with e.g. isInstance and cast.
Point-being, you can't validate generics at run-time without some extra work.
You can still program around it, depending on what you're doing. So for example, one simple way is just to rebuild each Map, the same way you rebuild the List:
static <K, V> List<Map<K, V>> castMapList(
Object obj, Class<K> keyType, Class<V> valueType) {
if (obj instanceof List<?>) {
List<Map<K, V>> result = new ArrayList<>();
for (Object element : (List<?>) obj) {
if (element instanceof Map<?, ?>) {
Map<K, V> map = new HashMap<>();
for (Map.Entry<?, ?> entry : (Map<?, ?>) element) {
map.put(keyType.cast(entry.getKey()),
valueType.cast(entry.getValue());
}
result.add(map);
} else {
// do something that you want?
// personally I would throw ClassCastExceptions
// but you seem to be returning null
}
}
return result;
}
return null;
}
Also, for example, it looks kind of like you seem to be doing some sort of deserialization, and you can bake the Class right in to the Map that gets serialized:
public class MyCheckedMap<K, V>
extends SomeMapImplementation<K, V>
implements Serializable {
private static final long serialVersionUID = 1L;
private final Class<K> keyType;
private final Class<V> valueType;
public MyCheckedMap(Class<K> keyType, Class<V> valueType) {
this.keyType = Objects.requireNonNull(keyType);
this.valueType = Objects.requireNonNull(valueType);
}
#SuppressWarnings("unchecked")
public static <K, V> MyCheckedMap<K, V> cast(
Object obj, Class<K> keyType, Class<V> valueType) {
if (!(obj instanceof MyCheckedMap<?, ?>))
throw new ClassCastException(obj.getClass().toString());
MyCheckedMap<?, ?> map = (MyCheckedMap<?, ?>) obj;
validate(keyType, map.keyType);
validate(valueType, map.valueType);
return (MyCheckedMap<K, V>) obj;
}
private static void validate(Class<?> lhs, Class<?> rhs) {
if (lhs == rhs)
return;
throw new ClassCastException(String.format("%s != %s", lhs, rhs));
}
}
You have to write some boiler-plate code for each List, Map, etc., but probably not much more than you seem to be doing.
There are also some other ways to approach this sort of problem, like Neal Gafter's Super Type Tokens and Guava's TypeToken, which is a derivative of Neal Gafter's idea. These essentially use reflection to achieve something like MyCheckedMap.
I'm not sure whether or not having unchecked warnings is an issue for you or not. For the map, you could do the following:
public <K, V> List<Map<K, V>> castMapList(Object object, Class<K> class1, Class<V> class2) {
if (object instanceof List<?>) {
List<Map<K, V>> result = new ArrayList<>();
for (Map<K, V> o : (List<Map<K, V>>) object) { //Unchecked cast
result.add(o);
}
return result;
}
return null;
}
or
for (Object o : (List<?>) object) {
if (o instanceof Map<?, ?>) {
result.add((Map<K, V>) o); //Unchecked cast
}
}
I don't think having a suppress warning is the worst case for you, since even in the cast method from Class.java, they do it. You must be sure that the list contains maps though.
This is what I came up with. Thanks again!
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CastUtility {
public static <T> List<T> castList(Object object, Class<T> clazz) {
if (object == null) {
return null;
}
if (object instanceof List<?>) {
List<T> result = new ArrayList<T>();
for (Object o : (List<?>) object) {
result.add(clazz.cast(o));
}
return result;
}
throw new ClassCastException();
}
public static <K, V> Map<K, V> castMap(Object object, Class<K> keyType,
Class<V> valueType) {
if (object == null) {
return null;
}
if (object instanceof Map<?, ?>) {
Map<K, V> result = new HashMap<K, V>();
for (Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
result.put(keyType.cast(entry.getKey()),
valueType.cast(entry.getValue()));
}
return result;
}
throw new ClassCastException();
}
public static <K, V> List<Map<K, V>> castMapList(Object object,
Class<K> keyType, Class<V> valueType) {
if (object == null) {
return null;
}
if (object instanceof List<?>) {
List<Map<K, V>> result = new ArrayList<Map<K, V>>();
for (Object o : (List<?>) object) {
result.add(castMap(o, keyType, valueType));
}
return result;
}
throw new ClassCastException();
}
}
Let's say I have the following Map which is created using Guava's library: (List<Integer> is also immutable)
Map<String, List<Integer>> map = ImmutableMap.builder()...
I pass this map to a class where I want to create a mutable copy of it and modify it. It is of course possible to do it manually, but is there a way to convert a nested immutable collection back to a mutable one?
As pointed out, I'd use an ImmutableListMultimap<String, Integer> instead of a ImmutableMap<String, ImmutableList<Integer>>.
Then if you want a mutable copy, you can just pass the immutable multimap to the create static factory method on one of the mutable ListMultimap implementations (ArrayListMultimap or LinkedListMultimap).
Here is my solution. There'e quite a lot of code required to set it up, but once it's done it's really easy to use.
public class Main {
// UnaryOperator and identity are in Java 8.
// I include them here in case you are using an earlier version.
static interface UnaryOperator<T> {
T apply(T t);
}
static <T> UnaryOperator<T> identity() {
return new UnaryOperator<T>() {
#Override
public T apply(T t) {
return t;
}
};
}
// This unary operator turns any List into an ArrayList.
static <E> UnaryOperator<List<E>> arrayList(final UnaryOperator<E> op) {
return new UnaryOperator<List<E>>() {
#Override
public List<E> apply(List<E> list) {
List<E> temp = new ArrayList<E>();
for (E e : list)
temp.add(op.apply(e));
return temp;
}
};
}
// This unary operator turns any Set into a HashSet.
static <E> UnaryOperator<Set<E>> hashSet(final UnaryOperator<E> op) {
return new UnaryOperator<Set<E>>() {
#Override
public Set<E> apply(Set<E> set) {
Set<E> temp = new HashSet<E>();
for (E e : set)
temp.add(op.apply(e));
return temp;
}
};
}
// This unary operator turns any Map into a HashMap.
static <K, V> UnaryOperator<Map<K, V>> hashMap(final UnaryOperator<K> op1, final UnaryOperator<V> op2) {
return new UnaryOperator<Map<K, V>>() {
#Override
public Map<K, V> apply(Map<K, V> map) {
Map<K, V> temp = new HashMap<K, V>();
for (Map.Entry<K, V> entry : map.entrySet())
temp.put(op1.apply(entry.getKey()), op2.apply(entry.getValue()));
return temp;
}
};
}
public static void main(String[] args) {
// In this example I will first create an unmodifiable collection of unmodifiable collections.
Map<String, List<Set<Integer>>> map = new HashMap<String, List<Set<Integer>>>();
map.put("Example", Collections.unmodifiableList(Arrays.asList(Collections.unmodifiableSet(new HashSet<Integer>(Arrays.asList(1, 2, 3))))));
map = Collections.unmodifiableMap(map);
// Now I will make it mutable in one line!
map = hashMap(Main.<String>identity(), arrayList(hashSet(Main.<Integer>identity()))).apply(map);
}
}
In Java, I want to add a getOrAdd method to a regular map, just like putIfAbsent on a ConcurrentHashMap.
Furthermore, for a certain key I want to store a list of items. Here's my attempt:
public class ListMap<K, V> extends HashMap<K, V> {
private HashMap<K, List<V>> map;
public ListMap() {
map = new HashMap<K, List<V>>();
}
public List<V> getOrAdd(K key) {
if (map.containsKey(key)) {
return map.get(key);
} else {
List<V> l = new ArrayList<V>();
map.put(key, l);
return l;
}
}
}
However, if someone wanted to iterate over a ListMap, he would need to cast the values explictly.
ListMap<Integer, MyClass> listMap = new ListMap<Integer, MyClass>();
for (Map.Entry<Integer, MyClass> entry : listMap.entrySet()) {
List<MyClass> val = (List<MyClass>) entry.getValue();
}
Is there a way of extending the HashMap class by some methods without creating a subclass? ( I've seen this in C#)
How can the ListMap class be modified such that one can get a ListMaps's value (List) without casting?
Instance of your class will be also HashMap so you don't need to, or even shouldn't add another field just to support getOrAdd method because other inherited and not overridden methods will not be referring to map field but to this instance.
So instead of adding separate field
private HashMap<K, List<V>> map;
change extending type of your ListMap to
public class ListMap<K, V> extends HashMap<K, List<V>>
^^^^^^^
and change your getOrAdd method to not use map field but this
public List<V> getOrAdd(K key) {
if (containsKey(key)) {
return get(key);
} else {
List<V> l = new ArrayList<V>();
put(key, l);
return l;
}
}
This change will let you use your map like
ListMap<Integer, String> listMap = new ListMap<Integer, String>();
for (Map.Entry<Integer, List<String>> entry : listMap.entrySet()) {
List<String> val = entry.getValue();//NO CASTING NEEDED
}
You can just extend HashMap like this:
public class ListMap<K, V> extends HashMap<K, List<V>> {
...
}
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 :)
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