I have a TreeMap in which I have stored some values. The map is sorted using the values, from highest to lowest. Now I want print out the contents of the TreeMap with their various indices.
If I have the following pairs in the map :
("Andrew", 10),
("John", 5),
("Don",9),
("Rolex", 30),
("Jack", 10),
("Dan",9)
I want to print out:
Rolex, 30 , 1
Jack, 10, 2
Andrew, 10, 2
Dan, 9, 4
Don, 9, 4
John, 5, 6.
This is what I've been trying but it doesn't seem to work well:
/**
*
* @author Andrew
*/
import java.util.*;
public class SortArray {
static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>>entriesSortedByValues(Map<K,V> map) {
SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
new Comparator<Map.Entry<K,V>>() {
@Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
int res = e1.getValue().compareTo(e2.getValue());
return res!= 0 ? res : 1;
//return e1.getValue().compareTo(e2.getValue());
}
});
sortedEntries.addAll(map.entrySet());
return sortedEntries;
}
public void test(){
Map mm = new TreeMap();
mm.put("Andrew", 11);
mm.put("Mbata", 21);
mm.put("Chinedu", 14);
mm.put("Bol", 14);
mm.put("Don", 51);
mm.put("Rolex", 16);
mm.put("Son", 41);
SortedSet newMap = entriesSortedByValues(mm);
Iterator iter = newMap.iterator();
int x = newMap.size();
List names = new ArrayList();
List scores = new ArrayList();
while(iter.hasNext()){
String details = iter.next().toString();
StringTokenizer st = new StringTokenizer(details, "=");
String name = st.nextToken();
names.add(name);
String score = st.nextToken();
scores.add(score);
//System.out.println(name + " Score:" +score + " Position:" + x);
x--;
}
Collections.reverse(names);
Collections.reverse(scores);
int pos = 1;
for(int i = 0; i<names.size();){
try{
int y = i+1;
if(scores.get(i).equals(scores.get(y))){
System.out.print("Name: "+ names.get(i)+"\t");
System.out.print("Score: "+ scores.get(i)+"\t");
System.out.println("Position: "+ String.valueOf(pos));
//pos++;
i++;
continue;
} else{
System.out.print("Name: "+ names.get(i)+"\t");
System.out.print("Score: "+ scores.get(i)+"\t");
System.out.println("Position: "+ String.valueOf(pos++));
}
i++;
} catch(IndexOutOfBoundsException e) {}
}
}
public SortArray(){
test();
}
public static void main(String [] args){
new SortArray();
}
}
First of all, Why are you catching that IndexOutOfBoundsException and doing nothing with it? if you run that you'll get that exception thrown (and I thing you already know it) the problem is in your algorithm inside the last "for" loop. I shouldn't give you the solution, but wth... at least you did some effort to make it run, so this is a more less working version:
import java.util.*;
public class SortArray {
static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>>entriesSortedByValues(Map<K,V> map) {
SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
new Comparator<Map.Entry<K,V>>() {
@Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
int res = e1.getValue().compareTo(e2.getValue());
return res != 0 ? res : 1;
//return e1.getValue().compareTo(e2.getValue());
}
});
sortedEntries.addAll(map.entrySet());
return sortedEntries;
}
public void test(){
Map mm = new TreeMap();
mm.put("Andrew", 11);
mm.put("Mbata", 21);
mm.put("Chinedu", 14);
mm.put("Bol", 14);
mm.put("Don", 51);
mm.put("Rolex", 16);
mm.put("Son", 41);
SortedSet newMap = entriesSortedByValues(mm);
Iterator iter = newMap.iterator();
int x = newMap.size();
List names = new ArrayList();
List scores = new ArrayList();
while(iter.hasNext()){
String details = iter.next().toString();
StringTokenizer st = new StringTokenizer(details, "=");
String name = st.nextToken();
names.add(name);
String score = st.nextToken();
scores.add(score);
//System.out.println(name + " Score:" +score + " Position:" + x);
x--;
}
Collections.reverse(names);
Collections.reverse(scores);
int pos;
int posBis = 0;
String lastScore = "";
for(int i = 0; i<names.size(); i++){
System.out.print("Name: "+ names.get(i)+"\t");
System.out.print("Score: "+ scores.get(i)+"\t");
if(i == 0 || !lastScore.equals(scores.get(i))) {
pos = i + 1;
posBis = pos;
} else {
pos = posBis;
}
System.out.println("Position: "+ String.valueOf(pos));
lastScore = (String)scores.get(i);
}
}
public SortArray(){
test();
}
public static void main(String [] args){
new SortArray();
}
}
Your SortedSet
is the wrong way to go about this. You can see in your Comparator
that it gets a bit messy when both values have to be looked up by the same key then you've got this messy (and incorrect) return res != 0 ? res : 1
return res != 0 ? res : 1
(the 1
should really be e1.getKey().compareTo(e2.getKey())
rather than always returning 1
).
A better way to go about this would be to just sort the keys yourself in a List
, rather than creating a separate SortedSet
. This way you don't have to worry about duplicate sorting values.
You can also abstract out the Comparator
stuff a little, to make it more reusable in other code later, if you need it.
import java.util.*;
public class PrintSomething {
public static <T extends Comparable<T>> Comparator<T> reverseComparator(final Comparator<T> oldComparator) {
return new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return oldComparator.compare(o2, o1);
}
};
}
public static <K,V extends Comparable<V>> Comparator<K> keyedComparator(final Map<K,V> lookup) {
return new Comparator<K>() {
@Override
public int compare(K o1, K o2) {
return lookup.get(o1).compareTo(lookup.get(o2));
}
};
}
public static void main(String[] args) {
Map<String, Integer> mm = new HashMap<>();
mm.put("Andrew", 10);
mm.put("John", 5);
mm.put("Don", 9);
mm.put("Rolex", 30);
mm.put("Jack", 10);
mm.put("Dan", 9);
Comparator<String> comparator = reverseComparator(keyedComparator(mm));
List<String> keys = Arrays.asList(mm.keySet().toArray(new String[mm.size()]));
//Collections.sort(keys); // optional, if you want the names to be alphabetical
Collections.sort(keys, comparator);
int rank = 1, count = 0;
Integer lastVal = null;
for (String key : keys) {
if (mm.get(key).equals(lastVal)) {
count++;
} else {
rank += count;
count = 1;
}
lastVal = mm.get(key);
System.out.println(key + ", " + mm.get(key) + ", " + rank);
}
}
}
In general things like SortedSet
make more sense when you need to keep the data itself sorted. When you just need to process something in a sorted manner one time they're usually more trouble than they're worth. (Also: is there any reason why you're using a TreeMap
? TreeMap
s sort their keys, but not by value, so in this case you're not taking advantage of that sorting. Using a HashMap
is more common in that case.)
You do a lot of work with the iterator, calling toString(), then splitting the results. And your Comparator is extra work too. Stay with a Map on both sides - you can use keys() and values() more directly, and let Java do the sorting for you. Most of your above code can be replaced with: (for clarity, I changed your name "mm" to "originalMap")
Map<Integer, String> inverseMap = new TreeMap<Integer, String>();
for (Map.Entry<String, Integer> entry : originalMap.entrySet()) {
inverseMap.put(entry.getValue(), entry.getKey());
}
Now, iterate over inverseMap to print the results. Note that if a count does exist twice in originalMap, only one will be printed, which is what you want. But which one gets printed left as an exercise for the reader :-). You might want to be more specific on that.
EDIT ADDED: If you do want to print out duplicate scores, this is not what you want. The original post I read said to skip if they were the same, but I don't see that after the edits, so I'm not sure if this is what OP wants.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.