I want to return a set of items with an attached score to the user so that the user can easily iterate from highest to lowest and get the n-th highest items.
Problems
- The JavaDoc of
SortedSet.first()
says "Returns the first (lowest) element currently in this set.". However in this case it is actually the highest score. If I compare the other way around, however, the user needs to reverse theSortedSet
in order to do this common use case. - If I only compare by score, the
SortedSet
can only have one item with the same score. However comparing byc
strikes me as somehow arbitrary (not sure what I can do against that). - Two Items with the same URI should not exist, however that is not prevented when they have different scores.
import java.util.SortedSet;
import java.util.TreeSet;
public class Main
{
static class Item<T extends Comparable<T>> implements Comparable<Item<T>>
{
double score;
T c;
public Item(double score, T c)
{
this.score=score;
this.c=c;
}
@Override public int compareTo(Item<T> i)
{
int x = -Double.compare(score, i.score);
if(x==0) {x=c.<T>compareTo(i.c);}
return x;
}
}
public static SortedSet<Item<String>> findItems()
{
SortedSet<Item<String>> items = new TreeSet<>();
items.add(new Item<String>(0.3,"honey"));
items.add(new Item<String>(0.7,"wolf"));
items.add(new Item<String>(0.5,"monitor"));
return items;
}
public static void main(String[] args)
{
// iterate from best to lowest score
for(Item item: findItems()) {}
// get item with best score
findItems().first();
}
}
1 Answer 1
The JavaDoc of SortedSet.first() says "Returns the first (lowest) element currently in this set.". However in this case it is actually the highest score.
The definition of lowest and highest is determined by the comparator. If you put Integers in the sorted set, the first element will be the lowest, because the default comparator will be used. In your Item
object however, you have a different definition of what is lower and higher:
@Override public int compareTo(Item<T> i) { int x = -Double.compare(score, i.score); if(x==0) {x=c.<T>compareTo(i.c);} return x; }
Your elements are still ordered from lowest to highest as dictated by your comparison method: practically you defined 0.7 to be lower than 0.3, that's all. As you noticed yourself, if you drop the negative sign in int x = -Double.compare(score, i.score);
you effectively switch the ordering.
If I compare the other way around, however, the user needs to reverse the SortedSet in order to do this common use case.
I'm wondering if you're aware of the .last()
method. It seems that's what you're looking for.
By the way, you can also make a TreeSet
sort items in reverse order, by constructing it like this:
SortedSet<Item<String>> items = new TreeSet<>(Collections.reverseOrder());
If I only compare by score, the SortedSet can only have one item with the same score. However comparing by c strikes me as somehow arbitrary (not sure what I can do against that).
I'm not really sure what you mean by that. Since c
is a String, comparison will be alphabetic by default.
I recommend this writing style (I also renamed variables):
@Override
public int compareTo(Item<T> item) {
int cmp = -Double.compare(score, item.score);
if (cmp == 0) {
return c.<T>compareTo(item.c);
}
return cmp;
}
Item
an incremental unique id? \$\endgroup\$-Integer.compare(a,b)
comparator, the lowest element would then beInteger.MAX_VALUE
. \$\endgroup\$