How to use put() in a subclass of Java's TreeMap - java

I want to create a subclass of class of java.util.TreeMap, to allow me to add an increment method:
public class CustomTreeMap<K, V> extends TreeMap<K, V> {
public void increment(Integer key) {
Integer intValue;
if (this.containsKey(key)) {
Object value = this.get(key);
if (!(value instanceof Integer)) {
// Fail gracefully
return;
} else {
intValue = (Integer) value;
intValue++;
}
} else {
intValue = 1;
}
this.put(key, intValue); // put(Integer, Integer) cannot be applied in TreeMap
}
}
Android Studio 1.0.2 first proposes put(K Key, V Value) for autocompletion, and later warns that:
put(K, V) cannot be applied in TreeMap to (java.lang.integer, java.lang.integer)
What is it that I am doing wrong?
See here for the solution I adopted.

If you want to create your custom treemap to handle Integers exclusively, you should make it extend TreeMap<K, Integer>, not the generic type V:
public class CustomTreeMap<K> extends TreeMap<K, Integer> {
...
}
This way you don't need the instanceof check later.
If your key also needs to be an Integer, declare no generic types instead:
public class CustomTreeMap extends TreeMap<Integer, Integer> {
...
}

If it should be Integer then use Integer:
public class CustomTreeMap<K> extends TreeMap<K, Integer> {
public void increment(K key) {
Integer intValue;
if (this.containsKey(key)) {
Object value = this.get(key);
if (!(value instanceof Integer)) {
// Fail gracefully
return;
} else {
intValue = (Integer) value;
intValue++;
}
} else {
intValue = 1;
}
this.put(key, intValue); // put(Integer, Integer) cannot be applied in TreeMap
}
}

Related

JAVA - Ordered HashMap Implementation with change key name function

I am trying to create a user interface with a HashMap. Users could change values and change the name of the keys without disturbing the order of the keys. I searched and found the LinkedHashMap. Which kept the order of the keys in most cases. But when I remove a key and add it back after renaming it, it always adds it to the end. So I've overridden LinkedHashMap class and added a changeKeyName() function.
Now it works (in my case) but I was wondering if it could be improved and made foolproof. I only overridden the functions I was using. What other functions have to be overridden in order make it complete?
Thanks in advance.
Here is the code:
private static class OrderedHashMap<K, V> extends LinkedHashMap<K, V> {
ArrayList<K> keys = new ArrayList<K>();
#Override
public V put(K key, V value) {
if (!keys.contains(key))
keys.add(key);
return super.put(key, value);
}
#Override
public V remove(Object key) {
keys.remove(key);
return super.remove(key);
}
#Override
public Set<K> keySet() {
LinkedHashSet<K> keys = new LinkedHashSet<K>();
for (K key : this.keys) {
keys.add(key);
}
return keys;
}
public void changeKeyName(K oldKeyName, K newKeyName) {
int index = keys.indexOf(oldKeyName);
keys.add(index, newKeyName);
keys.remove(keys.get(index + 1));
V value = super.get(oldKeyName);
super.remove(oldKeyName);
super.put(newKeyName, value);
}
#Override
public Set<Map.Entry<K, V>> entrySet() {
final OrderedHashMap<K, V> copy = this;
LinkedHashSet<Map.Entry<K, V>> keys = new LinkedHashSet<Map.Entry<K, V>>();
for (final K key : this.keys) {
final V value = super.get(key);
keys.add(new Map.Entry<K, V>() {
#Override
public K getKey() {
return key;
}
#Override
public V getValue() {
return value;
}
#Override
public V setValue(V value) {
return copy.put(getKey(), value);
}
});
}
return keys;
}
}
EDIT: I think the why wasn't clear enough. Let's say we added the keys below.
{"key1":"value1"},
{"key2":"value2"},
{"key3":"value3"},
{"key4":"value4"}
And for example I want to change the key name of the "key2". But as this is also a user interface, order of the keys should stay the same.
I made some research and I found out that apart from removing the key and re-puting the new key name with the same value, nothing could be done. So if we do that and change "key2" to "key2a":
{"key1":"value1"},
{"key3":"value3"},
{"key4":"value4"},
{"key2a":"value2"}
And what I want is this:
{"key1":"value1"},
{"key2a":"value2"},
{"key3":"value3"},
{"key4":"value4"}
So I just kept the keys in a ArrayList and returned them when entrySet() and keySet() methods are called.
Have you considered simply using the TreeMap class instead of a custom subclass of LinkedHashMap? It will maintain order if you implement the Comparable interface on the keys.
If you want to be able to change keys without affecting the hashing function in the collection where the value is stored try a custom class such as;
private class VariableKeyMap {
private LinkedHashSet<K, V> myCollection = new LinkedHashSet<K, V>();
private HashMap<int, K> aliases = new HashMap<int, K>();
int id = 0;
public void addEntry(K key, V value) {
id += 1;
aliases.put(K, id);
myCollection.put(id, V);
}
public V getValue(K key) {
return myCollection.get(aliases.get(key));
}
...
}
The you can update your key alias without affecting where the value is actually stored;
public void changeKey(K oldKey, K newKey) {
int currentId = aliases.get(oldKey);
aliases.remove(oldKey);
aliases.put(newKey, currentId);
}

How to declare final HashMap that should not allow to update or remove element

I have a hash map like,
public static void main(String[] args) {
final Map<String, String> daysMap = new HashMap(7);
daysMap.put("1", "Sunday");
daysMap.put("2", "Monday");
daysMap.put("3", "Tuesday");
daysMap.put("4", "Wednesday");
daysMap.put("5", "Thursday");
daysMap.put("6", "Friday");
daysMap.put("7", "Saturday");
}
In this map
1. Should not allow to put more than 7 elements
2. Should not update value for corresponding key [like daysMap.put("5", "xxx");]
3. Should not allow to remove any key
How to do?
You can implement a new HashMap
public class CoolMap<K, V> extends HashMap<K, V> {
#Override
public V put(K key, V value) {
if (size() == 7) {
throw new IllegalStateException("Size is at max!");
} else {
// If there is something already with that key
if (containsKey(value)) {
// do nothing
return value;
} else {
// put inside
return super.put(key, value);
}
}
}
#Override
public void putAll(Map<? extends K, ? extends V> collection) {
if (collection.size() > 7) {
throw new IllegalStateException("Size is at max!");
} else {
super.putAll(collection);
}
}
#Override
public V remove(Object key) {
return null;// doesn't remove anything
}
Points 2 and 3 are covered by Collections.unmodifiableMap. To cover the first point, you can add an hand written test.
As has been already discussed ,the Points 2 and 3 are covered like this
import java.util.*;
public class OP2 {
public static void main(String[] s) {
//object hash table
Hashtable<String,String> table = new Hashtable<String,String>();
table.
// populate the table
table.put("1", "Sunday");
table.put("2", "Monday");
table.put("3", "Tuesday");
table.put("4", "Wednesday");
table.put("5", "Thursday");
table.put("6", "Friday");
table.put("7", "Saturday");
System.out.println("Initial collection: "+table);
// create unmodifiable map
Map m = Collections.unmodifiableMap(table);
// try to modify the collection
// m.put("key3", "value3");
//Uncomment the above line and an error is obtained
}
}
Moreover for the first problem it would be better to call a function when you populate your map:-
public boolean putAndTest(MyKey key, MyValue value) {
if (map.size() >= MAX && !map.containsKey(key)) {
return false;
} else {
map.put("Whatever you want");
return true;
}
}
Why don´t you create your own object that contains a private hashmap and then you can allow what methods on that private hashmap that are made public?

How to treat keys of HashMap as optional in java [duplicate]

This question already has answers here:
HashMap to return default value for non-found keys?
(16 answers)
Closed 9 years ago.
While using Map as a function argument, only values for 3 keys are populated. However when this function is invoked in another function, the user populates values for initial 2 keys and he does not require 3rd key to be assigned with any value. However if 3rd key is not assigned any value then, the 3rd key is display null value.
Is there any way to avoid this. if user does not assign any value to 3rd key, it must be empty instead of null value.
public String setMapValues(Map<String,String> testMap) throws Exception
{
String str="";
str= testMap.get("a");
str+=testMap.get("b");
str+=testMap.get("c");
info(str);
return str;
}
public void run() throws Exception
{
LinkedHashMap<String,String> myMap = new LinkedHashMap<String,String>();
myMap.put("a", "James");
myMap.put("b", "Bond");
this.setMapValues(myMap);
}
The function calls displays JamesBondnull as the output, instead it should only display JamesBond as the output by ignoring/skipping the null at the end.
You can use a function like
static String nullAsEmpty(Object o) {
return o == null ? "" : o.toString();
}
public String setMapValues(Map<String,String> testMap) {
String str = nullAsEmpty(testMap.get("a")) +
nullAsEmpty(testMap.get("b")) +
nullAsEmpty(testMap.get("c"));
info(str);
return str;
}
How about:
String temp = testMap.get("c");
str+= (temp == null : "" : temp);
You can implement your version of Map:
import java.util.HashMap;
class MyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
V val = super.get(key);
if (val != null) {
return val;
} else {
return "";
}
}
}
Then just use MyMap instead of Map
Or init your map with default values, if you know all keys which could be null
Map getInstance(){
Map<String,String> myMap = new LinkedHashMap<String,String>();
myMap.put("a", "");
myMap.put("b", "");
myMap.put("b", "");
return myMap;
}
By putting duplicate keys the old values are replaced by the new ones.
To be complete:
If you use Vadchens answer - which is better - you can do two things:
Extends your map by setting generic value-parameter directly to String
class MySecMap<K> extends LinkedHashMap<K, String>{
#Override
public String get(Object key) {
String val = super.get(key);
if (val != null) {
return val;
} else {
return "";
}
}
}
Or create a class with an extra interface and a default-value-provider:
interface IDefaultValueProvider<V>{
V getDefaultValue();
}
class MyMap<K, V, D extends IDefaultValueProvider<V>> extends LinkedHashMap<K, V>{
private IDefaultValueProvider<V> provider;
public MyMap(IDefaultValueProvider<V> p){
super();
provider = p;
}
#Override
public V get(Object key) {
V val = super.get(key);
if (val != null) {
return val;
} else {
return this.provider.getDefaultValue();
}
}
}

A sorted ComputingMap?

How can I construct a SortedMap on top of Guava's computing map (or vice versa)? I want the sorted map keys as well as computing values on-the-fly.
The simplest is probably to use a ConcurrentSkipListMap and the memoizer idiom (see JCiP), rather than relying on the pre-built unsorted types from MapMaker. An example that you could use as a basis is a decorator implementation.
May be you can do something like this.It's not a complete implementation.Just a sample to convey the idea.
public class SortedComputingMap<K, V> extends TreeMap<K, V> {
private Function<K, V> function;
private int maxSize;
public SortedComputingMap(int maxSize, Function<K, V> function) {
this.function = function;
this.maxSize = maxSize;
}
#Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
#Override
public void putAll(Map<? extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
#Override
public V get(Object key) {
V tmp = null;
K Key = (K) key;
if ((tmp = super.get(key)) == null) {
super.put(Key, function.apply(Key));
}
if (size() > maxSize)
pollFirstEntry();
return tmp;
}
public static void main(String[] args) {
Map<Integer, Long> sortedMap = new SortedComputingMap<Integer, Long>(3,
new Function<Integer, Long>() {
#Override
public Long apply(Integer n) {
Long fact = 1l;
while (n != 0)
fact *= n--;
return fact;
}
});
sortedMap.get(12);
sortedMap.get(1);
sortedMap.get(2);
sortedMap.get(5);
System.out.println(sortedMap.entrySet());
}
}
If you need the thread safety, this could be tricky, but if you don't I'd recommend something close to Emil's suggestion, but using a ForwardingSortedMap rather than extending TreeMap directly.

Searching in a TreeMap (Java)

I need to do a search in a map of maps and return the keys this element belong.
I think this implementation is very slow, can you help me to optimize it?.
I need to use TreeSet and I can't use contains because they use compareTo, and equals/compareTo pair are implemented in an incompatible way and I can't change that.
(sorry my bad english)
Map<Key, Map<SubKey, Set<Element>>> m = new TreeSet();
public String getKeys(Element element) {
for(Entry<Key, Map<SubKey, Set<Element>>> e : m.entrySet()) {
mapSubKey = e.getValue();
for(Entry<SubKey, Set<Element>> e2 : mapSubKey.entrySet()) {
setElements = e2.getValue();
for(Element elem : setElements)
if(elem.equals(element)) return "Key: " + e.getKey() + " SubKey: " + e2.getKey();
}
}
}
The problem here is that the keys and values are backward.
Maps allow one to efficiently find a value (which would be Key and SubKey) associated with a key (Element, in this example).
Going backwards is slow.
There are bi-directional map implementations, like Google Collections BiMap, that support faster access in both directions—but that would mean replacing TreeMap. Otherwise, maintain two maps, one for each direction.
if you can't use contains, and you're stuck using a Map of Maps, then your only real option is to iterate, as you are doing.
alternatively, you could keep a reverse map of Element to Key/SubKey in a separate map, which would make reverse lookups faster.
also, if you're not sure that a given Element can exist in only one place, you might want to store and retrieve a List<Element> instead of just an Element
Using TreeMap and TreeSet work properly when compareTo and equals are implemented in such a way that they are compatible with each other. Furthermore, when searching in a Map, only the search for the key will be efficient (for a TreeMap O(log n)). When searching for a value in a Map, the complexity will become linear.
There is a way to optimize the search in the inner TreeSet though, when implementing your own Comparator for the Element type. This way you can implement your own compareTo method without changing the Element object itself.
Here is a bidirectional TreeMap (or Bijection over TreeMap).
It associates two overloaded TreeMaps Which are tied together.
Each one "inverse" constant field points to the other TreeMap. Any change on one TreeMap is automatically reflected on its inverse.
As a result, each value is unique.
public class BiTreeMap<K, V> extends TreeMap<K, V> {
public final BiTreeMap<V, K> inverse;
private BiTreeMap(BiTreeMap inverse) {
this.inverse = inverse;
}
public BiTreeMap() {
inverse = new BiTreeMap<V, K>(this);
}
public BiTreeMap(Map<? extends K, ? extends V> m) {
inverse = new BiTreeMap<V, K>(this);
putAll(m);
}
public BiTreeMap(Comparator<? super K> comparator) {
super(comparator);
inverse = new BiTreeMap<V, K>(this);
}
public BiTreeMap(Comparator<? super K> comparatorK, Comparator<? super V> comparatorV) {
super(comparatorK);
inverse = new BiTreeMap<V, K>(this, comparatorV);
}
private BiTreeMap(BiTreeMap<V, K> inverse, Comparator<? super K> comparatorK) {
super(comparatorK);
this.inverse = inverse;
}
#Override
public V put(K key, V value) {
if(key == null || value == null) {
throw new NullPointerException();
}
V oldValue = super.put(key, value);
if (oldValue != null && inverse._compareKeys(value, oldValue) != 0) {
inverse._remove(oldValue);
}
K inverseOldKey = inverse._put(value, key);
if (inverseOldKey != null && _compareKeys(key, inverseOldKey) != 0) {
super.remove(inverseOldKey);
}
return oldValue;
}
private int _compareKeys(K k1, K k2) {
Comparator<? super K> c = comparator();
if (c == null) {
Comparable<? super K> ck1 = (Comparable<? super K>) k1;
return ck1.compareTo(k2);
} else {
return c.compare(k1, k2);
}
}
private V _put(K key, V value) {
return super.put(key, value);
}
#Override
public V remove(Object key) {
V value = super.remove(key);
inverse._remove(value);
return value;
}
private V _remove(Object key) {
return super.remove(key);
}
#Override
public void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
K key = e.getKey();
V value = e.getValue();
put(key, value);
}
}
#Override
public void clear() {
super.clear();
inverse._clear();
}
private void _clear() {
super.clear();
}
public boolean containsValue(Object value) {
return inverse.containsKey(value);
}
#Override
public Map.Entry<K, V> pollFirstEntry() {
Map.Entry<K, V> entry = super.pollFirstEntry();
inverse._remove(entry.getValue());
return entry;
}
#Override
public Map.Entry<K, V> pollLastEntry() {
Map.Entry<K, V> entry = super.pollLastEntry();
inverse._remove(entry.getValue());
return entry;
}
}

Categories