I have two maps in my class (I am new to generics)
private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();
private Map<Integer, Short> bMap = new HashMap<Integer, Short>();
If key does not exist in map I want to get a zero value. So I have made this wrapper method to minimize typing containsKey(key)
#SuppressWarnings("unchecked")
private <T extends Number> T getValue (Map<Integer, T> map, Integer key) {
return (T) ((map.containsKey(key)) ? map.get(key) : 0);
}
I call it like
Integer a = getValue(aMap, 15); //okay in any case
Short b = getValue(bMap, 15); //15 key does not exist
For second case it gives me:
ClassCastException: java.lang.Integer cannot be cast to java.lang.Short
So probably I would need to do something like : new Number(0), but Number is abstract.
How can I fix it?
EDIT:
My idea is to do arithmetic operations without additional ifs:
Integer a = getValue(aMap, 15);
a = a + 10;
One way is to supply the default value as an argument to your function:
private <T extends Number> T getValue (Map<Integer, T> map, Integer key, T dflt) {
return (T) ((map.containsKey(key)) ? map.get(key) : dflt);
}
public static void main(String[] args) {
Integer a = getValue(aMap, 15, 0); //okay in any case
Short b = getValue(bMap, 15, (short)0); //15 key does not exist
}
Well, you can't do much about that without also providing T in a way that code can look at.
The simplest approach at that point would probably be to keep a map of 0 values:
private static Map<Class<?>, Number> ZERO_VALUES = createZeroValues();
private static Map<Class<?>, Number> createZeroValues() {
Map<Class<?>, Number> ret = new HashMap<Class<?>, Number>();
ret.put(Integer.class, (int) 0);
ret.put(Short.class, (short) 0);
ret.put(Long.class, (long) 0);
// etc
}
Then:
private <T extends Number> T getValue (Map<Integer, T> map, Integer key, Class<T> clazz) {
return clazz.cast(map.containsKey(key) ? map.get(key) : ZERO_VALUES.get(clazz));
}
Then you'd unfortunately have to call it as:
Short b = getValue(bMap, 15, Short.class);
Basically this is a limitation of Java generics :(
In situations like this, I just override the get() method:
private Map<Integer, Short> bMap = new HashMap<Integer, Short>() {
#Override
public Short get(Object key) {
return containsKey(key) ? super.get(key) : new Short(0);
}
};
Then you can just use it anywhere and it will behave as you specified.
Cast 0 to short explicitly as it will be int by default. And then convert to Short wrapper. In java u cannot directly convert from primitive to a Wrapper of a different type.
Related
I'm attempting to define a method putIfGreaterThan() for my new Map class (given a key it replaces the old value with the new value only if the new value is greater than the old value).
I understand I could accomplish this either via composition (by having a private final Map<String, Double> map; in new class and then passing a Map to constructor) or by implementing the Map interface in my new class and passing a Map to the constructor (although I'm not sure which approach is superior).
My main problem is that I need to be able to call the method putIfGreaterThan() on <String, Double> and <String, Integer> but not on <String, String> (bec it doesn't make sense to call it on <String, String> ). If I use generics ( <K, V>) the client can pass a <String, String> which is not allowed. On the other hand if I allow a Double I will not be able to pass an Integer or vice versa. How can I define a method to allow either Integer or Double but not String?
Note: I'm unable to define two constructors (one for Double and one for Integer) bec I get the error: Erasure of method XX is the same as another method in type XX .
You can use the decorator pattern and limit the generics to subtypes of Number, which you can than compare without casting with a little trick taken from this answer. It takes the string representation of the number instances and creates an instance of BigDecimal - thus circumventing casting of any kind.
Below you find the relevant implementation details of the decorator, of course you'll need to override the remaining methods of the Map interface.
public class GreaterThanDecorator<K, V extends Number> implements Map<K, V> {
private final Map<K, V> delegate;
public GreaterThanDecorator(Map<K, V> delegate) {
this.delegate = delegate;
}
public V putIfGreaterThan(K key, V value) {
V old = delegate.get(key);
if (old == null) {
delegate.put(key, value);
return null;
}
BigDecimal newValue = new BigDecimal(value.toString());
BigDecimal oldValue = new BigDecimal(old.toString());
if (newValue.compareTo(oldValue) >= 1)
old = delegate.put(key, value);
return old;
}
}
Feel free to forbid the other subtypes of Number, i.e. by throwing an exception, as you see fit.
Generics and numbers don't go together well, unfortunately. But you can do something like this:
public class MyMap {
private final Map<String, Number> map = new HashMap<>();
public void putInt(String key, int value) {
map.put(key, value);
}
public void putDouble(String key, int value) {
map.put(key, value);
}
public void putIfGreaterThan(String key, Number value) {
if (value instanceof Double) {
double doubleValue = (Double) value;
map.compute(key, (k, v) -> {
if (!(v instanceof Double) || v.doubleValue() > doubleValue) {
return v;
} else {
return value
}
});
} else if (value instanceof Integer) {
int intValue = (Integer) value;
map.compute(key, (k, v) -> {
if (!(v instanceof Integer) || v.intValue() > intValue) {
return v;
} else {
return value
}
});
} else {
throw new IllegalArgumentException("Expected Double or Integer, but got " + value);
}
}
}
Ideally you'd want to declare a method introducing a type parameter which extends V & Comparable<? super V>, but that is not valid Java.
However, you can define a static method without using the &.
public static <K, V extends Comparable<? super V>> void putIfGreater(
Map<K,V> map, K key, V value
) {
V old = map.get(key);
if (old != null && value.compareTo(old) > 0) {
map.put(key, value);
}
// Or:
//map.computeIfPresent(key, (k, old) -> value.compareTo(old) > 0 ? value : old);
}
If you were going down the route of different construction, then the type would need to be different for the case where V is Comparable<? super V>. If two constructors have the same erasure, it's probably time to have meaningfully named static creation methods.
This question already has answers here:
Can't cast to to unspecific nested type with generics
(5 answers)
Closed 6 years ago.
I have a method with the following signature:
public <T> int numberOfValues(Map<T, Set<?>> map)
However I can’t call it passing in a Map<String, Set<String>>. For instance, the following doesn’t compile:
Map<String, Set<String>> map = new HashMap<>();
numberOfValues(map);
The error message being that:
numberOfValues (java.util.Map<java.lang.String,java.util.Set<?>>) in class cannot be applied to (java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)
However, if I change to the following all is fine:
public <T, V> int numberOfValues(Map<T, Set<V>> map)
However I’m not at all interested in V, as I just want to know the size of each of the sets.
For completeness sake, this is the whole method:
public <T, V> int numberOfValues(Map<T, Set<V>> map) {
int n = 0;
for (T key : map.keySet()) {
n += map.get(key).size();
}
return n;
}
Which I’m aware it can also be accomplished like this, but isn’t the point of the question :)
public <T> int numberOfValues(Map<?, Set<T>> map) {
int n = 0;
for (Set<T> value : map.values()) {
n += value.size();
}
return n;
}
Update: yet another way of achieving the same
public <T> int numberOfValues(Map<?, Set<T>> map) {
int n = 0;
for (Object key : map.keySet()) {
n += map.get(key).size();
}
return n;
}
Final update:
Thanks to Jorn’s answer, this is the final implementation...
public int numberOfValues(Map<?, ? extends Set<?>> map) {
int n = 0;
for (Set<?> value : map.values()) {
n += value.size();
}
return n;
}
You're missing the fact that Set<?> is also used as a generic parameter. And generics are invariant. i.e. when the parameter is Map<String, Set<?>>, the passed argument must be exactly Map<String, Set<?> (or a subtype of). Whereas with Set<V> the type argument is inferred.
You can solve this by using a bounded wildcard:
public <T> int numberOfValues(Map<T, ? extends Set<?>> map) {
...
}
Hint:
In your numberOfValues method, it is completely legal to write something like
Set<Object> set = new HashSet<>();
set.add(1);
set.add("Powned");
map.put(null, set);
You can replace the null with a suitable key already in the map to make a valid mapping, utterly breaking the type safety in the surrounding code.
Either infer or explicitly define the type of the set, or add a wildcard bound preventing mutating map access (like ? extends Set<?>)
I am currently working with Generics and I'm not that experienced. I got this problem:
I made this class to compare 2 values implementing Comparable and do some other stuff, let's say incrementing the compareTo() value:
public final class Type<T extends Comparable<T>> {
public int compare(T val1, T val2) {
int compTemp = val1.compareTo(val2);
compTemp++;
// do stuff
return compTemp;
}
}
Now I create some instances of Type:
Type<Integer> t1 = new Type<Integer>();
Type<String> t2 = new Type<String>();
And now I want to put them into a Map. As I cannot predict the generic type of Type I use the wilcard ?:
Map<String, Type<?>> map = Maps.newHashMap();
map.put("t1", t1);
map.put("t2", t2);
If I want to invocate Type.compare() it's working for t1, but not for map.get(t1):
t1.compare(1,1); // compiles fine
map.get("t1").compare(1, 1); // does not compile
The latter throws a compilation error:
The method compare(capture#3-of ?, capture#3-of ?) in the type
Type is not applicable for the arguments (int, int)
I know it's because of my wildcard parameter, but I don't exactly know why and how to fix this.
The only "fix" I see is to use raw types for the Map, however this will show several warnings.
I suppose right now I have a huge misunderstanding of the wilcard parameter.
I appreciate every answer!
You are using a the wildcard <?> in your Map means your Map does not know the exact type. For the Map they are all stored as Type<Object>.
One thing you can do here is some type unsafe casting
((Type<Integer>)map.get("t1")).compare(1, 1); // does compile but is type unsafe
Another solution would be to store the generic Class when creating your Type Objects in constructor. Then you can do some type safe casting in your compare method using another method generic Type. See this Type class
public final class Type<T extends Comparable<T>> {
Class<T> type;
Type(Class<T> _type){
type = _type;
}
public <E extends Comparable<E>> int compare(E val1, E val2) {
T v1 = type.cast(val1);
T v2 = type.cast(val2);
int compTemp = v1.compareTo(v2);
compTemp++;
// do stuff
return compTemp;
}
}
Now you can do this:
Type<Integer> t1 = new Type<Integer>(Integer.class);
Type<String> t2 = new Type<String>(String.class);
Map<String, Type<?>> map = new HashMap<>();
map.put("t1", t1);
map.put("t2", t2);
map.get("t1").compare(1, 1); // compiles fine
map.get("t2").compare("one", "one"); // compiles fine
I assume you want to do something like this at runtime:
String key = getKeyFromParams(p1, p2);
map.get( key ).compare(p1, p2);
Since the compiler doesn't know the generic type of the value it returns for the key you can't just call compare() that way.
You could, however, restructure your code a bit to make it work (still with warnings and the need to take care not to break anything):
Make your compare() method accept Comparable parameters
Pass the class of the generic type to the constructor and use it to check the parameters in compare()
if the parameter types are ok compare them
if they don't match the type class throw an exception
use the type class as the map key and use it for the lookup
Example:
public final class Type<T extends Comparable<T>> {
private final Class<T> typeClass;
public Type( Class<T> typeClass) {
this.typeClass = typeClass;
}
public int compare(Comparable val1, Comparable val2) {
if( !(typeClass.isInstance( val1 ) && typeClass.isInstance( val2 ) ) ) {
throw new IllegalArgumentException("message");
}
int compTemp = val1.compareTo(val2);
compTemp++;
// do stuff
return compTemp;
}
//getter for the typeClass
}
A wrapper for the map:
class TypeComparator {
Map<Class<?>, Type<?>> map = new HashMap<Class<?>, Test.Type<?>>();
public void addType(Type<?> type) {
map.put( type.getTypeClass(), type );
}
public <T extends Comparable<T>> void compare(T p1, T p2) {
map.get( p1.getClass() ).compare( p1, p2 );
}
}
And finally call it like this:
//add some types
TypeComparator comp = new TypeComparator();
comp.addType( new Type<Integer>( Integer.class ));
comp.addType( new Type<String>( String.class ));
//compare ints
comp.compare( 1, 1 );
//compare Strings
comp.compare( "1", "1" );
//won't compile
comp.compare( "1", 1 );
Some final thoughts:
You have to handle cases where there is no type in the map (e.g. if one passed Double parameters in the example)
You'll still get warnings but if you hide them well in the implementation and take care not to apply casts on the wrong types you should be fine.
The problem is that the following code can't compile if the generic signature consists of several ? which are the same type.
import java.util.Map;
import java.util.HashMap;
import java.util.function.Function;
public class Test {
private static <T> T findSelfReference(Map<T, T> map) {
for (Map.Entry<T, T> entry : map.entrySet()) {
if (entry.getKey() == entry.getValue()) {
return entry.getKey();
}
}
return null;
}
private static <T> T findSelfReference2(Map<T, T> map) {
for (T key : map.keySet()) {
if (map.get(key) == key) {
return key;
}
}
return null;
}
// Question: How to write the method signature that can ensure compile-time type safety? Both the signatures fail to compile.
// private static <T> String fun(Function<Map<T, T>, T> finder) {
private static String fun(Function<Map<?, ?>, ?> finder) {
Map<Integer, Integer> map1 = new HashMap<>();
// some processing to map1
Integer n = finder.apply(map1); // usage here, compile-time type checking wanted
Map<String, String> map2 = new HashMap<>();
// other processing to map2 depending on n
return finder.apply(finder, map2); // another usage
}
public static void main(String[] args) {
// Please don't change into helper class...
System.out.println(fun(Test::findSelfReference));
System.out.println(fun(Test::findSelfReference2));
}
}
In fun, inside each call to finder.apply() the type T is fixed. But among different calls they use different types. I tried the wildcard capture (ref: here) but no luck.
I don't want to cast the result into Object in which the checking has to be made in runtime. All type checking should be done in compile time.
Is it possible without making O(n) helper classes where n is the number of inline function?
The problem is that you want the finder's apply method to be generic.
The solution is to define your own functional interface with a generic method.
#FunctionalInterface
interface Finder {
<T> T apply(Map<T, T> map);
}
private static String fun(Finder finder) {
// same body
}
If this counts as a "helper class" then I don't know what to tell you. You are trying to pound a square peg in to a round hole. Function#apply isn't a generic method so you can't do this with Function.
What exactly are you trying to achieve with your method fun?
You pass it a Function with either wildcards or with a type parameter T and then you want to apply it on two different, concrete types (Integer and String).
This is not going to work:
Map<Integer, Integer> map1 = new HashMap<>();
// ...
Integer n = finder.apply(map1);
Because here you expect finder to take the type Integer, but you've specified in the declaration of fun that either the type is unknown (if you declare it with ?) or with some unbounded type T (if you declare it with a type parameter T). But if you're going to apply it to Integer you need a Function<Map<Integer, Integer>>, not a Function<Map<?, ?>, ?> or a Function<Map<T, T>, T> for some unbounded type T.
You could write it like this - but the method fun itself is more or less useless.
private static <T> T fun(Map<T, T> map, Function<Map<T, T>, T> finder) {
return finder.apply(map);
}
public static void main(String[] args) {
Map<Integer, Integer> map1 = new HashMap<>();
map1.put(1, 2);
map1.put(3, 4);
map1.put(5, 7);
map1.put(2, 2);
map1.put(8, 8);
Integer n = fun(map1, Test::findSelfReference);
String a = String.valueOf(n + 1);
Map<String, String> map2 = new HashMap<>();
map2.put("1", "2");
map2.put("3", "4");
map2.put("5", "7");
map2.put("3", a);
String s = fun(map2, Test::findSelfReference2);
System.out.println(n);
System.out.println(s);
}
Well you can do something like this:
private static <T> T fun(Function<Map<T, T>, T> finder, Map<T, T> map) {
return finder.apply(map);
}
private static Map<String, String> getStringMap(Integer n) {
String a = String.valueOf(n + 1);
Map<String, String> map2 = new HashMap<>();
map2.put("1", "2");
map2.put("3", "4");
map2.put("5", "7");
map2.put("3", a);
return map2;
}
private static Map<Integer, Integer> getIntMap() {
Map<Integer, Integer> map1 = new HashMap<>();
map1.put(1, 2);
map1.put(3, 4);
map1.put(5, 7);
map1.put(2, 2);
map1.put(8, 8);
return map1;
}
public static void main(String[] args) {
fun(Test::findSelfReference, getIntMap());
fun(Test::findSelfReference, getStringMap(1));
}
I dont see the purpose of fun method, when you can always do this:
Function<Map<Integer, Integer>, Integer> m = Test::findSelfReference;
m.apply(getIntMap());
I apologize if this question is a duplicate, searching was difficult as I was unsure of the proper name for what I'm trying to accomplish. The simplest explanation would be
List<A>, List<B> into Map<Key, Tuple<A,B>> where A.Key matched B.Key
To clarify: I have a list of A object and B object that share a key. I'd like to then correlate these two lists into a map where the key matches into a map of key, and tuple A,B.
I've played around with many ideas on how to do this in my head, but most of them end with me feeling like I've misused the library (such as Maps.uniqueIndex, and Iterables.transform). Can anyone point me in the right direction?
There are no tuple (pair etc.) implementations in Guava. (It's another discussion if it's good idea to implementation tuples in Java at all.) The natural mapping I would suggest is to use a Multimap:
List<A> as = Lists.newArrayList(new A(1, "a"), new A(3, "c"), new A(2, "b"));
List<B> bs = Lists.newArrayList(new B(1, 2), new B(3, 6), new B(5, 10));
Function<WithKey, Object> toKey = new Function<WithKey, Object>() {
#Override public Object apply(WithKey input) { return input.key(); }
};
ImmutableListMultimap<Object, AbstractWithKey> index =
Multimaps.index(Iterables.concat(as, bs), toKey);
or
Multimap<Object, WithKey> m = ArrayListMultimap.create();
for (WithKey w : Iterables.concat(as, bs)) m.put(w.key(), w);
You have to check your invariants before using the multimap (or while your iterating over the multimap entries) for example there could be keys with only a A or B instance. (This shouldn't be a performance issue as it can be done lazily with Iterables.filter.)
Duplicates of one type is another issue. You could check them or use a HashMultimap to ignore them. You could even build a multimap with a constrainted set for values that checks that a value is unique (see Multimaps.newSetMultimap(Map> map, Supplier> factory) and Constraints.constrainedSet(Set set, Constraint constraint)). This has the advantage that it fails fast.
With these A and B implementations:
interface WithKey {
Object key();
}
abstract class AbstractWithKey implements WithKey {
Object key;
Object v;
#Override public Object key() { return key; }
#Override public String toString() {
return MoreObjects.toStringHelper(this).add("k", key).add("v", v).toString();
}
}
class A extends AbstractWithKey {
public A(int i, String v) {
key = i;
this.v = v;
}
}
class B extends AbstractWithKey {
public B(int i, int v) {
key = i;
this.v = v;
}
}
the output is:
{1=[A{k=1, v=a}, B{k=1, v=2}], 2=[A{k=2, v=b}], 3=[A{k=3, v=c}, B{k=3,
v=6}], 5=[B{k=5, v=10}]}
Update:
If you have to end up with your tuple instances, you can transform the Multimap.
Multimap<Object, WithKey> m = ArrayListMultimap.create();
for (WithKey w : Iterables.concat(as, bs)) m.put(w.key(), w);
Function<Collection<WithKey>, Tuple> f =
new Function<Collection<WithKey>, Tuple>(){
#Override public Tuple apply(Collection<WithKey> input) {
Iterator<WithKey> iterator = input.iterator();
return new Tuple(iterator.next(), iterator.next());
} };
Map<Object, Tuple> result = Maps.transformValues(m.asMap(), f);
Output ((a,b) is the tuple syntax):
{1=(A{k=1, v=a},B{k=1, v=2}), 3=(A{k=3, v=c},B{k=3, v=6})}
Are you guaranteed that keys are unique? (That is, that no two A's have the same key?)
If so, I'd write something like the following:
Map<Key, A> aMap = Maps.uniqueIndex(theAs, aKeyFunction); // Guava!
Map<Key, B> bMap = Maps.uniqueIndex(theBs, bKeyFunction);
Map<Key, AWithMatchingB> joinedMap = Maps.newHashMap();
for(Map.Entry<Key, A> aEntry : aMap.entrySet()) {
joinedMap.put(aEntry.getKey(), AWithMatchingB.match(
aEntry.getValue(), bMap.get(aEntry.getKey())));
}
If you're not guaranteed that aMap.keySet().equals(bMap.keySet()), then you'd modify this appropriately: check whether or not there's a matching B or not, etc.
Sorting the lists by key and transforming the two lists to tuples without much help from Guava is quite readable:
Comparator<WithKey>c = new Comparator<WithKey>(){
#Override public int compare(WithKey o1, WithKey o2) {
return o1.key().compareTo(o2.key());
}
};
Collections.sort(as, c);
Collections.sort(bs, c);
Preconditions.checkArgument(as.size() == bs.size());
Iterator<A> aIt = as.iterator();
Iterator<B> bIt = bs.iterator();
Map<Integer, Tuple> ts = Maps.newHashMap();
while(aIt.hasNext()) {
A a = aIt.next();
B b = bIt.next();
Preconditions.checkArgument(a.key().equals(b.key()));
ts.put(a.key(), new Tuple(a, b));
}
Output ((a,b) is the tuple syntax):
{1=(A{k=1, v=a},B{k=1, v=2}), 3=(A{k=3, v=c},B{k=3, v=6})}
This can be implemented nicer when Guava supports zip similar to Python:
sa = [(1, "a"), (3, "c")]
sb = [(1, 2), (3, 6)]
sa.sort()
sb.sort()
vs = [(a[0], (a,b)) for (a, b) in zip(sa, sb)]