Sorting Hashed maps Map<String,?> - java

I am trying to sort and get the top 3 maxium values for the VALUE(?)
Map<String,?> points = map
Tried using Map<String, String> and it works fine, but in this case I need Map<String, ?>.

The type of the values will need to implement Comparable.
If the size of the map is not too large, you could try something like this:
private <T extends Comparable> SortedSet<T> sortValues(final Map<?, T> m)
{
final SortedSet<T> result = new TreeSet<>();
result.addAll(m.values());
return result;
}
I haven't tested this, but I think I have all of the type declarations correct. Then just take the first, or last as the case may be, elements from the sorted set. I believe you can optionally provide your own Comparator to the set to choose how to order the objects.

You cannot sort non-Comparable objects, to begin with, so you'll need a map with Comparable values, not ?. Having that, however, you could do something like this:
public static <T extends Comparable<? super T>> List<T> sortValues(Map<?, T> map) {
List<T> buffer = new ArrayList<T>(map.values());
Collections.sort(buffer);
return(buffer);
}
Then you can do whatever you want with the return value of sortValues, such as picking the first three objects in it.

Related

Java: Check variable type

I'm working on an assignment where I have to sort integers and string type variables.
So I have a method declared as
public void quickSort(ArrayList<Entry<Integer, String>> list) {
but if I want to overload the method and use
public void quickSort(ArrayList<Entry<String, Integer>> list) {
It is recognized as a duplicate.
Is there a way to check what my variable types are?
Alternatively, is there a way to sort strings and integers the same way so that I can do something like
public void quickSort(ArrayList<Entry<K, V>> list) {
that will work on both date types?
You probably need to provide an explicit comparator, since there is no natural ordering of Entrys:
public <T> void quickSort(ArrayList<T> list, Comparator<? super T> comparator)
and then use that comparator inside the method for your comparisons.
The fact that you are trying to sort something with two type parameters (Map.Entry<String, Integer> or Map.Entry<Integer, String>) is not relevant from a generics perspective: it's just a single type, hence you can replace the whole thing with T.
What also may be helpful for you is that you can check the datatype of a variable with instanceof keyword. E.g.
if (variable instanceof String) {
//do stuff
}

Sort Hashmap values compareTo method

I am writing the below code to sort the hash map values :
private static HashMap sortByValues(HashMap map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return (((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
}
});
}
However When I execute this, it throws an error stating cannot find symbol compareTo. But isnt the method in the String class which is in the lang package?
Also when i replace it by adding a Comparable typecast, it runs fine
private static HashMap sortByValues(HashMap map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
}
});
}
Can someone please help, I am a beginner in Collections.
All of Java's collection classes and interfaces are generics, which means they are intended to be used with type parameters. For historical reasons it's possible to use them without type parameters, which is what you've done here. However, it's a bad idea to do that, because
you sacrifice a lot of the type safety that the compiler gives you,
you end up casting objects to whatever type you need them to be, which is prone to error, and such errors usually only reveal themselves at run time.
Finding errors at compile time is better than finding them at run time. The compiler is your friend - it gives you messages to help you find your errors.
Now in this particular case, you seem to be building a sorted list of values from your map. But it only makes sense to do this if the values in your map belong to some type that can be sorted. There's no general way of sorting Object, so for this to make sense, you want to restrict your parameter to be a map whose values can be sorted, or to put it another way, can be compared to other objects of the same type.
The generic interface that tells you that one object can be compared to another is Comparable. So if you have a type V, then writing V extends Comparable<V> means that one object of type V can be compared to other objects of type V, using the compareTo method. This is the condition that you want the type of the values in your map to obey. You don't need any such condition on the type of the keys in your map.
Therefore, you could write your method as generic, which means that its signature will list some type parameters, inside < > characters, possibly with some conditions on those type parameters. In your case, you'd give your method a signature like this, assuming it's going to return a List.
private static <K, V extends Comparable<V>> List<V> sortAndListValues(Map<K,V> map)
Of course, if you really intend to return some kind of sorted map, then it might be more like
private static <K, V extends Comparable<V>> Map<K,V> sortByValues(Map<K,V> map)
but you need to remember that it's not possible to sort HashMap objects. They're naturally sorted in an order that's implied by the hashCode function of the key class, and by the current size of the map. This is generally not a very useful order. There are other types of map in the JDK, such as
TreeMap, which sorts its entries according to the key - not what you want here
LinkedHashMap, which sorts its entries according to the order they were inserted - and you could probably make use of this here.
For the sake of answering your question though, I'm just going to write the List version of your method.
private static <K, V extends Comparable<V>> List<V> sortAndListValues(Map<K,V> map) {
List<V> toReturn = new LinkedList<>(map.values());
Collections.sort(toReturn, new Comparator<V>() {
public int compare(V first, V second) {
return first.compareTo(second);
}
});
return toReturn;
}
Note that by using the type parameters K and V wherever it's appropriate to do so, there's no need for any kind of casting. The compiler will also warn you if you try to use any of the objects in the map in a way that's inappropriate for their type.
There are shorter ways of writing this of course, using the "functional style" that comes with Java 8. But that's a topic for another post entirely.
#ghostrider - you have removed generics from HashMap so both key and value are of Object type. Inside contents of map are Comparable type but the reference is of Entry<Object, Object> not Entry<Object, Comparable>. Look into the below example.
Object obj = new Integer(5);
int i = obj.intValue(); // Error
int i = ((Integer)obj).intValue(); // Success
Here int i = obj.intValue(); fails but int i = ((Integer)obj).intValue(); get success because i am explicitly type casting because of reference is of Object type.
You can do this by following
private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap) {
// 1. Convert Map to List of Map
List<Map.Entry<String, Integer>> list =
new LinkedList<Map.Entry<String, Integer>>(unsortMap.entrySet());
// 2. Sort list with Collections.sort(), provide a custom Comparator
// Try switch the o1 o2 position for a different order
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1,
Map.Entry<String, Integer> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
});
// 3. Loop the sorted list and put it into a new insertion order Map LinkedHashMap
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Map.Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}

Java 8 multiple mapping

Is it possible perform multiple mapping on collection?
Following code compilation error:
... in Stream cannot be applied to java.util.function.Function<capture<?>,capture<?>>
private static List<?> multipleMapping(final Collection<?> collection, final List<Function<?, ?>> functions) {
Stream<?> stream = collection.stream();
for (Function<?, ?> function : functions) {
stream = stream.map(function);
}
return stream.collect(Collectors.toList());
}
I would like to generic solution.
The problem comes from the fact that you're using a generic wildcard ?. What you want is to have a parameterized type T, that will represent the type of the Stream element. Assuming the function would return the same type as their input, you could have:
private static <T> List<T> multipleMapping(final Collection<T> collection, final List<Function<T, T>> functions) {
Stream<T> stream = collection.stream();
for (Function<T, T> function : functions) {
stream = stream.map(function);
}
return stream.collect(Collectors.toList());
}
This compiles fine: the mapper given to map correcly accepts a T and returns a T. However, if the functions don't return the same type as their input then you won't be able to keep type-safety and will have to resort to using List<Function<Object, Object>>.
Note that we could use a UnaryOperator<T> instead of Function<T, T>.
Also, you could avoid the for loop and reduce all functions into a single one using andThen:
private static <T> List<T> multipleMapping(final Collection<T> collection, final List<Function<T, T>> functions) {
return collection.stream()
.map(functions.stream().reduce(Function.identity(), Function::andThen))
.collect(Collectors.toList());
}
If you have few functions (i.e. if you can write them down), then I suggest you don't add them to a list. Instead, compose them into a single function, and then apply that single function to each element of the given collection.
Your multipleMapping() method would now receive a single function:
public static <T, R> List<R> multipleMapping(
Collection<T> collection, Function<T, R> function) {
return collection.stream()
.map(function)
.collect(Collectors.toList());
}
Then, in the calling code, you could create a function composed of many functions (you will have all the functions anyway) and invoke the multipleMapping() method with that function.
For example, suppose we have a list of candidates:
List<String> candidates = Arrays.asList(
"Hillary", "Donald",
"Bernie", "Ted", "John");
And four functions:
Function<String, Integer> f1 = String::length;
Function<Integer, Long> f2 = i -> i * 10_000L;
Function<Long, LocalDate> f3 = LocalDate::ofEpochDay;
Function<LocalDate, Integer> f4 = LocalDate::getYear;
These functions can be used to compose a new function, as follows:
Function<String, Integer> function = f1.andThen(f2).andThen(f3).andThen(f4);
Or also this way:
Function<String, Integer> composed = f4.compose(f3).compose(f2).compose(f1);
Now, you can invoke your multipleMapping() method with the list of candidates and the composed function:
List<Integer> scores = multipleMapping(candidates, function);
So we have transformed our list of candidates into a list of scores, by explicitly composing a new function from four different functions and applying this composed function to each candidate.
If you want to know who will win the election, you could check which candidate has the highest score, but I will let that as an exercise for whoever is interested in politics ;)

Detail about the "super" wildcard in java generics

I have a question regarding generics:
Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();
with super it's possible to instantiate a HashMap<Object,Object> for a <? super String>.
However then you can add only objects which extends String ( in this case only String itself).
Why don't they forbid by compilation error as well as happens with the extends wildcard.
I mean if once created a Map <Object, Object> it's possible only to add Strings.. why not forcing to create a Map<String, String> in the first place? (like it happens with the extends wildcard)
Again I know the difference between super and extends concerning generics. I would like just to know the details I have aboved-mentioned.
Thanks in advance.
Let's use List instead of Map for brevity.
Essentially, practical meaning of extends and super can be defined as follows:
List<? extends T> means "a List you can get T from"
List<? super T> means "a List you can put T into"
Now you can see that there is nothing special about extends - behavior of extends and super is completely symmetric:
List<? extends Object> a = new ArrayList<String>(); // Valid, you can get an Object from List<String>
List<? extends String> b = new ArrayList<Object>(); // Invalid, there is no guarantee that List<Object> contains only Strings
List<? super String> a = new ArrayList<Object>(); // Valid, you can put a String into List<Object>
List<? super Object> b = new ArrayList<String>(); // Invalid, you cannot put arbitrary Object into List<String>
I think you are thrown off because you picked a collection type. Collections are rarely used as consumers and thus a lower bound (? super X) is not put on their element types. A more appropriate example is predicate.
Consider a method such as <E> List<E> filter(List<? extends E> p, Predicate<? super E> p). It will take a list l and a predicate p and return a new list containing all elements of l which satisfy p.
You could pass in a List<Integer> and a Predicate<Number> which is satisfied by all multiples of 2.5. The Predicate<Number> would become a Predicate<? super Integer>. If it did not, you could not invoke filter as follows.
List<Integer> x = filter(Arrays.asList(1,5,8,10), Predicates.multipleOf(2.5));
Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();
Since Java Generics are based on type erasure, with this line you didn't create a MashMap<Object,Object>. You just created an instance of the HashMap class; the type parameters get lost immediately after this line of code and all that stays is the type of your mappa1 variable, which doesn't even mention Object. The type of the new expression is assignment-compatible with the type of mappa1 so the compiler allows the assignment.
In general, the type parameters used with new are irrelevant and to address this issue, Java 7 has introduced the diamond operator <>. All that really matters is the type of mappa1, which is is Map<? super String, ? super String>; as far as the rest of your code is concerned, this is the type of the instantiated map.
The problem you're describing doesn't exist.
It is because your reference is declared as Map<? super String, ? super String>. But your actual object can hold any object since it's HashMap<Object,Object>
Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();
map1.put("", "");
//you can put only string to map1
//but you can do this
Map map2 = map1;
map2.put(23, 234);
the same can be described by a better example:
String a = "a".
a.length(); // legal.
Object b = a;
b.length() // compilation error

Why does Java Map<K, V> take an untyped parameter for the get and remove methods?

I ran into a bug in my code where I was using the wrong key to fetch something from a Java map that I believed was strongly typed using Java generics. When looking at the Map Javadocs, many of the methods, including get and remove, take an Object as the parameter instead of type K (for a Map defined as Map). Why is this? Is there a good reason or is it an API design flaw?
I think this is for backwards compatibility with older versions of the Map interface. It's unfortunate that this is the case however as you're right, it would be much better if this took the correct type.
Because the map will return a value if the object passed to the get method is equal to any key stored in the map. Equal does not mean that they have to be of the same type, but that the key's and the passed object's equal methods are implemented in such a way, that the different object types are mutually recognized as equal.
The same applies of course to the remove method.
Example of valid code, which would break (not compile) if the get method only allowed parameters of type K:
LinkedList<Number> k1 = new LinkedList<Number>();
k1.add(10);
ArrayList<Integer> k2 = new ArrayList<Integer>();
k2.add(10);
Map<LinkedList<Number>, String> map = new HashMap<LinkedList<Number>, String>();
map.put(k1, "foo");
System.out.println(map.get(k2));
This was done so that if the type parameter is a wildcard, these methods can still be called.
If you have a Map<?, ?>, Java won't allow you to call any methods that are declared with the generic types as arguments. This prevents you from violating the type constraints so you cannot, for instance, call put(key, value) with the wrong types.
If get() were defined as get(K key) instead of the current get(Object key), it too would have been excluded due to this same rule. This would make a wildcarded Map practically unusable.
In theory, the same applies to remove(), as removing an object can never violate the type constraints either.
Here is an example of code that would not have been possible if get had been declared as get(T key):
public static <K,V> Map<K, V> intersect(Map<? extends K, ? extends V> m1, Map<? extends K, ? extends V> m2) {
Map<K,V> result = new HashMap<K, V>();
for (Map.Entry<? extends K, ? extends V> e1 : m1.entrySet()) {
V value = m2.get(e1.getKey()); // this would not work in case of Map.get(K key)
if (e1.getValue().equals(value)) {
result.put(e1.getKey(), e1.getValue());
}
}
return result;
}
e1.getKey() returns an object of some unknown subtype of K (the subtype used by m1), but m2 uses a potentially different subtype of K. Had Map.get() been declared as get(K key), this usage would not have been allowed.

Categories