You could try something like this. If two or more values are tied, they are ordered by the keys instead.
public final class RankableMap<K extends Comparable<K>, V extends Comparable<V>> extends TreeMap<K, V> {
private static final class Pair<K extends Comparable<K>, V extends Comparable<V>> implements Comparable<Pair<K, V>> {
private final K k;
private final V v;
private Pair(K k, V v) {
this.k = k;
this.v = v;
}
@Override
public int compareTo(Pair<K, V> that) {
int a = v.compareTo(that.v);
return a != 0 ? a : k.compareTo(that.k);
}
}
private final SortedSet<Pair<K, V>> set = new TreeSet<>();
@Override
public V put(K k, V v) {
V v2 = super.put(k, v);
if (v.equals(v2))
return v2;
if (v2 != null)
set.remove(new Pair<>(k, v2));
set.add(new Pair<>(k, v));
return v2;
}
@Override
public V remove(Object k) {
V v = super.remove(k);
if (v != null)
set.remove(new Pair<>((K) k, v));
return v;
}
@Override
public void clear() {
super.clear();
set.clear();
}
public int rank(K k) {
return 1 + set.headSet(new Pair<K, V>(k, get(k))).size();
}
}
I tested this class with the following code
RankableMap<String, Integer> map = new RankableMap<>();
map.put("quayside", 95);
map.put("surrogate", 340)
map.put("merchantman", 181);
map.put("foo", 340);
map.put("bar", 42);
for (String key : map.keySet())
System.out.println(key + " rank = " + map.rank(key));
map.remove("bar");
System.out.println();
for (String key : map.keySet())
System.out.println(key + " rank = " + map.rank(key));
and it gave the following result:
bar rank = 1
foo rank = 4
merchantman rank = 3
quayside rank = 2
surrogate rank = 5
foo rank = 3
merchantman rank = 2
quayside rank = 1
surrogate rank = 4