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>> {
...
}
Related
I am trying to sort a hashmap that has a structure of by the value from high to Low.
I have created a function below to sort the data.
public static void SortDataHighToLow (Map <String, Integer> UnsortedMap){
List <Integer> list = new ArrayList(UnsortedMap.keySet());
Collections.sort(list,new Comparator <Integer>(){
#Override
public int compare(Integer arg0, Integer arg1) {
return arg0-arg1;
}
});
Map <String, Integer> sortedMap = new LinkedHashMap<>();
for (Integer keys: list){
sortedMap.put(UnsortedMap.toString(), keys);
}
System.out.println(sortedMap);
}
I am recieving the error below:
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
I believe my error is caused by the for() above that I cannot read the Key value.
What adjustment should I make?
Thanks for the help.
Upon the answer came from #deHaar my problem got resolved. The code is below.
private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Object>() {
#SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
}
});
Map<K, V> result = new LinkedHashMap<>();
for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}
This is a comparator that do the job:
public class MapKeyByValueComparator<K, T> implements Comparator<K> {
private final Map<K, T> map;
private final Comparator<T> comparator;
public MapKeyByValueComparator(Map<K, T> map, Comparator<T> comparator) {
this.map = map;
this.comparator = comparator;
}
#Override
public int compare(K o1, K o2) {
int ritem = comparator.compare(map.get(o1), map.get(o2));
// CAN NOT RETURNS 0, otherwise key with the same value will be overridden
if (ritem == 0) {
ritem = 1;
}
return ritem;
}
}
And then you can use a TreeMap as:
Map<something, somethig> map = new TreeMap<>(comparator):
map.addAll(...);
But PAY ATTENTION, this brokes the contract of Comparable
It is strongly recommended (though not required) that natural orderings be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.
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);
}
}
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;
}
let's imagine this scenario - I would like to use TreeMap in java. It's part of the Colletions framework and the only implementation of the SortedMap interface.
public class MyDictionary extends TreeMap<String, String> {
// some code
}
In order to walk through the entries stored in my Dictionary class I will need a type of Map.Entry. Somewhere in the code (could be a method of the MyDictionary class or even more likely a method in the wrapper class containing a variable of MyDictionary class holding my data) there will be something like:
public void showEntries() {
for (Map.Entry<String, String> e : dictionary) {
System.out.println(e.getKey(), e.getValue()); // do something
}
}
And now the question: is there a way to bind the generic types of Map.Entry to the generic types declared for the TreeMap?
The goal is to have the generic types defined in one place only.
In case I decide to change the type of data held in the TreeMap later I won't have to search all places where I used those types.
The example above is a Proof-Of-Concept. Pls help.
You can make the MyDictionary class generic, with type parameters to match TreeMap.
public class MyDictionary<K, V> extends TreeMap<K, V>
Then you can refer to those type parameters throughout your class. Specifically:
for (Map.Entry<K, V> e : dictionary) {
Or if you know that the key and the value will always be the same type:
public class MyDictionary<E> extends TreeMap<E, E>
and
for (Map.Entry<E, E> e : dictionary) {
This can be achieved by using two adapters - one for Entry and one for Iterator.
First, inside Dictionary, you create your Entry adapter, e.g.:
public static class Entry implements Map.Entry<String, String> {
private Map.Entry<String, String> entry;
Entry(Map.Entry<String, String> entry) {
this.entry = entry;
}
#Override
public String getKey() {
return entry.getKey();
}
#Override
public String getValue() {
return entry.getValue();
}
#Override
public String setValue(String value) {
return entry.setValue(value);
}
}
To be able to use your Dictionary class in the foreach loop, you have to implement Iterable interface. TreeMap does not implement it.
public class Dictionary extends TreeMap<String, String> implements Iterable<Dictionary.Entry>
You can write your iterator() method like this:
#Override
public Iterator<Entry> iterator() {
return new Iterator<Entry>() {
Iterator<Map.Entry<String, String>> wrapped = entrySet().iterator();
#Override
public boolean hasNext() {
return wrapped.hasNext();
}
#Override
public Entry next() {
return new Entry(wrapped.next());
}
#Override
public void remove() {
wrapped.remove();
}
};
}
Now, you can enjoy using foreach loops without generic types:
for (Dictionary.Entry e : dictionary) {
System.out.println(e.getKey() + " " + e.getValue());
}
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 :)