1

Maybe my logic stopped working for a bit, but I find this behavior confusing. Suppose I have a TreeMap as follows:

  TreeMap<Integer, Double> map = new TreeMap<Integer, Double>(Collections.reverseOrder());
    map.put(123, 0.5);
    map.put(678, 1.0);
    map.put(567, 0.1);

    int key = 100;

Now, I want to use the map.lowerEntry(key) and according to the documentation of the getter:

Returns a key-value mapping associated with the greatest key strictly less than the given key, or null if there is no such key.

I would expect null as a result, but the system call System.out.println("Lower than "+key+ " is "+map.lowerEntry(key)); produces Lower than 100 is 123=0.5. If I use map.higherEntry(key) which is documented as

Returns a key-value mapping associated with the least key strictly greater than the given key, or null if there is no such key.

then I get the desired result, but it is counter-intuitive based on the documentation. I guess calling the Collections.reverseOrder() comparator on the TreeMap inverts the logic applied inside the map?

EDIT Using a TreeSet I get consistent behavior (regarding the function names) when it is initialized with the Collections.reverseOrder() comparator:

TreeSet<Integer> set = new TreeSet<Integer>(Collections.reverseOrder());
    set.add(123);
    set.add(678);
    set.add(567);

int key = 200;
System.out.println("Lower than "+key+ " is "+set.tailSet(key));

produces Lower than 200 is [123] which I would expect. Is this inconsistency between TreeMap and TreeSet normal? Can someone please explain please? Thank you.

Marius
  • 990
  • 1
  • 14
  • 34
  • 3
    Of course `Collections.reverseOrder()` will reverse the definition of lower/higher - just as the name says. – Thomas Jun 18 '15 at 09:01
  • Well I guess that should be specified in the documentation or a flag should be put in place in case the order is reversed to keep the consistency – Marius Jun 18 '15 at 09:02
  • 1
    Well keep in mind that `reverseOrder()` is just a special implementation of `Comparator` that will reverse the natural order of the keys (as defined by their implementation of `compareTo()`). There might be a multitude of different comparators or comparable implementations for a multitude of situations and you can't handle all those cases in the tree implementation or even documentation. In fact the tree map doesn't even know the comparator you passed reverses the order. – Thomas Jun 18 '15 at 09:11
  • I am aware of the multitude of comparators and so on, but then why the implementation of the `TreeSet set = new TreeSet(Collections.reverseOrder())` is still able to be consistent while using `set.tailSet(key)` and `set.headSet(key)` even though underlying there is still a TreeMap with the `Collections` comparator? – Marius Jun 18 '15 at 09:22
  • I'm not sure what you mean with "still able to be consistent" here but `TreeSet` works the same way as `TreeMap`. `headSet()` will return the "lower" values and `tailSet()` the "higher" values based on the comparator. – Thomas Jun 18 '15 at 09:27
  • Nope. That is the thing. Try the example I've put in the edit. If I use `tailSet()` I get the values that are lower than my specified `key`, even though the set is initialized with `Collections.reversedOrder()` – Marius Jun 18 '15 at 09:29
  • 2
    As I said, `tailSet()` returns the "higher" values as defined by the comparator. Thus it is similar to `higherEntry()` (you iterate from lower to higher or from head to tail). The error lies in using `tailSet()` instead of `headSet()` here. – Thomas Jun 18 '15 at 09:30
  • You state that you get the desired result using higherEntry, which is what? Null? – mjs Jun 18 '15 at 09:33
  • @momo yeah, as stated I would expect `null` – Marius Jun 18 '15 at 09:38
  • @Thomas and isn't it logical if you say `headSet` to return you the subset of values "higher than" a certain value... being a HEAD set and tailSet to return you the "lower than" values ? being the TAIL of the set? I have to have the order of the elements reversed in the TreeSet so that the actual names of those getters are consistent with the results they produce. In any case, thank you for your time – Marius Jun 18 '15 at 09:42
  • 1
    Well head = front, tail = back. If you iterate over an ordered list you normally start at the front where the lower values are defined (i.e. ascending iteration is default in most situations) so head->tail = lower->higher. As stated multiple times: it all depends on the definition of "lower", which in case of a reverse comparator is reversed from the default. – Thomas Jun 18 '15 at 09:55
  • @Thomas Hehehe.. we understand each other, there is no issue there, but I find the documentation and/or names of the functions misleading – Marius Jun 18 '15 at 10:00

2 Answers2

2

I'll try and "summarize" the comments above for some structure:

TreeMap.lowerEntry(key) and TreeSet.headSet(key) have basically the same behavior which is they return the elements which are lower than the key. The same holds for TreeMap.higherEntry(key) and TreeSet.tailSet(key).

Using a reverse comparator on either of them would cause the order to be reversed, i.e. higher becomes lower and lower becomes higher. This might seem counterintuitive when the keys/elements are numbers etc. but so would be reversing their sort order.

If you add some semantics to the elements it might be easier to understand:

Let's say we're dealing with a ticketing system that counts the occurence of errors while some person defines priorities for the errors to be fixed.

Now you want to iterate through that list of errors and display them, so you sort them using a TreeMap<Integer, ErrorDescription>.

Depending on the semantics of the key you'd use a different comparator:

  • If the key is priority, where common conception is lower numbers mean higher priority, you'd just order the map from smallest to highest number, i.e. you just use the standard order.
  • If the key is number of occurences you'd most probably want to have the errors with the highest occurences be at the head of the list and thus you'd use a reverse order. However, since integers are defined to be ordered small to high you'd to employ a reversing comparator.

Last, a quote from your last comment:

I find the documentation and/or names of the functions misleading.

If you consider the example above just adding semantics to numbers would change the meaning of "lower" and "higher". Additionally take into account that numeric keys are just one type of keys, you could also use dates, strings, enums or a multitude of other objects which have a natural order (thus implementing Comparable) or need to be ordered on a case-by-case basis (thus using a Comparator).

Because there are so many different situations the documentation has to apply to it's hard to write it in a way that fits everything. The meaning of "lower" and "higher" depends on the situation anyway and thus it's easiest to just use that wording and leave the interpretation of the semantics to the developer.

Thomas
  • 87,414
  • 12
  • 119
  • 157
0

Probably the answer you want is here: TreeMap(Comparator<? super K> comparator) constructor

Constructs a new, empty tree map, ordered according to the given comparator ...

Parameters:

comparator - the comparator that will be used to order this map. If null, the natural ordering of the keys will be used.

Then, you should read less than and greater than only with respect to this comparator.

Also, as Thomas has pointed out, you're probably confusing with the meaning of tailSet(key). From the doc, it should give you elements that are greater than or equal to the key. So, in the the reverse order, you get lower values.

dejvuth
  • 6,986
  • 3
  • 33
  • 36
  • Hi dejvuth. Thanks for your time. Can you please logically explain me what is for you a tailSet and what is a headSet, regardless of the java documentation – Marius Jun 18 '15 at 09:45
  • 1
    `tailSet` and `headSet` give you a `SortedSet`, which order you elements from head to tail. In natural ordering, it's in ascending order. – dejvuth Jun 18 '15 at 10:01