I am using Java 1.7 and Eclipse Juno.
I have a list of objects that can be sorted by any one of the object's properties. To accomplish the sort I put into a map the list index of each object with the value of the property that is selected as the sort. The map is then sorted by value and then a new list assembled by looking up the objects in the original list and adding them to a new list.
There are other ways to go about sorting a list by different properties, but this is the approach taken by some code I am cleaning up, and I can't change it too drastically (it is part of a single method many thousands of lines long, with lots of what are effectively global variables. I'm refactoring as time allows, but there are no tests, so it's slow going).
I want to avoid explicit typecasting, raw types, and unchecked cast/invocations.
The program below illustrates the idea. It works, but produces raw types and unchecked conversion warnings. I could suppress the warnings, but I would prefer if there were a way to declare it such that it does not generate a warning and also is compatible with the sortByValue()
function.
Also, I noticed that when I write:
Map<Integer,Comparable> b = sortByValue(map);
for( Integer index : b.keySet()){
output.add(animals.get(index));
}
The type of .keySet()
is the expected Set<Integer>
. But if I write:
for( Integer index : sortByValue(map).keySet()){
output.add(animals.get(index));
}
Then the type of .keySet()
is unparameterized Set
, and I get a type mismatch error. I can use type Object and explicit cast index to Integer, but I don't understand why the return type of keySet()
should be different.
package my.sandbox;
import java.util.*;
public class Main {
public static void main(String[] args) {
try {
test();
} catch (Exception e) {
e.printStackTrace();
}
}
// https://stackoverflow.com/a/2581754/145446
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
});
Map<K, V> result = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
static class Animal {
String Name;
Double Weight;
Integer Age;
Animal(String n, Double w, Integer a){
Name = n;
Weight = w;
Age = a;
}
public String toString(){
return String.format("%10s : %6.1f %d", Name, Weight, Age);
}
}
private static void test() {
List<Animal> animals = new LinkedList<Animal>();
animals.add(new Animal("Spot", 35.4, 5));
animals.add(new Animal("Rover", 47.2, 3));
animals.add(new Animal("Phydeaux", 55.2, 4));
int sortOption = new Random(System.currentTimeMillis()).nextInt(3);
// ** Two 'raw types' warnings here
Map<Integer, Comparable> map = new HashMap<Integer, Comparable>();
for( Integer i =0; i<animals.size(); i++)
{
switch (sortOption) {
case 0 :
map.put(i, animals.get(i).Name);
break;
case 1 :
map.put(i, animals.get(i).Weight);
break;
case 2 :
map.put(i, animals.get(i).Age);
break;
}
}
List<Animal> output = new LinkedList<Animal>();
// ** One each 'raw types' and unchecked conversion warnings here
for( Object index : sortByValue(map).keySet()){
output.add(animals.get((Integer)index));
}
for( Animal s : output)
System.out.println(s);
}
}
Edit:
After exploring a number of options I began to wonder why the map was being used at all. I just need to compare objects in different ways, so why not just build an appropriate Comparator, then sort the list directly?
private static void test() {
List<Animal> animals = new LinkedList<Animal>();
animals.add(new Animal("Spot", 35.4, 5));
animals.add(new Animal("Rover", 47.2, 3));
animals.add(new Animal("Phydeaux", 55.2, 4));
final int sortOption = new Random(System.currentTimeMillis()).nextInt(3);
// Build a comparator for the specified sort
Collections.sort(animals, new Comparator<Animal>() {
public int compare(Animal o1, Animal o2) {
switch (sortOption){
case 0:
return (o1.Name).compareTo(o2.Name);
case 1:
return (o1.Weight).compareTo(o2.Weight);
case 2:
return (o1.Age).compareTo(o2.Age);
}
return 0;
}});
for( Animal s : animals)
System.out.println(s);
}