1

I'm new to the Guava library and trying to use some of its classes to simplify my code. I've run across the need to sort a Map by value. A quick search found this post which posts an accepted answer as the following code snippet:

Ordering<Map.Entry<Key, Value>> entryOrdering = Ordering.from(valueComparator)
  .onResultOf(new Function<Entry<Key, Value>, Value>() {
    public Value apply(Entry<Key, Value> entry) {
      return entry.getValue();
    }
  }).reverse();
// Desired entries in desired order.  Put them in an ImmutableMap in this order.
ImmutableMap.Builder<Key, Value> builder = ImmutableMap.builder();
for (Entry<Key, Value> entry : 
    entryOrdering.sortedCopy(map.entrySet())) {
  builder.put(entry.getKey(), entry.getValue());
}
return builder.build();
// ImmutableMap iterates over the entries in the desired order

Can someone please clarify for me how this works? I am failing to understand something.

The definition of Ordering is Ordering<T>. In this case <T> would be Map.Entry. Furthermore, the method signature of onResultOf is onResultOf(Function<F,? extends T> function).

In the above code, onResultOf is being called with the following parameter:

onResultOf(new Function<Entry<Key, Value>, Value>())

Which in this case would imply that:

<F> = Entry<Key, Value>
<? extends T> = Value

Which in turn implies that Value is a type that would extend a Map.Entry.

How is that possible? How can Value be a type that would extend Map.Entry, when the entry map contains Key, Value?

I am sure I am misreading or misunderstanding something, but if anyone can shed some light on this, I am hoping it will help me understand Guava and the Ordering() class a little better. Especially how the onResultOf(Function) works.

Community
  • 1
  • 1
Eric B.
  • 23,425
  • 50
  • 169
  • 316

3 Answers3

3

There are three different Orderings being created here, one of which has a different type than the others. Your code code be rewritten as:

Ordering<Value> valueOrdering = Ordering.from(valueComparator);
Ordering<Map.Entry<Key, Value>> entryOrdering = valueOrdering
     .onResultOf(new Function<Entry<Key, Value>, Value>() {
         public Value apply(Entry<Key, Value> entry) {
            return entry.getValue();
         }
});
Ordering<Map.Entry<Key, Value>> finalOrdering = entryOrdering.reverse();
Michael Krussel
  • 2,586
  • 1
  • 14
  • 16
2

The method signature of onResultOf is:

public <F> Ordering<F> onResultOf(Function<F,? extends T> function)

The F takes the place of T. To match the previous definition it should be rewritten as

public <T> Ordering<T> onResultOf(Function<T,? extends U> function)

So the parameter to onResultOf is actually Function< Map.Entry<Key, Value>, ? extends U > and therefore, the second parameter doesn't have to extend Map.Entry

agbinfo
  • 793
  • 5
  • 17
1

Maybe you are confused by the fact, that the class Ordering is declared as Ordering<T>, but the return value of the onResultOf method is Ordering<F>.

The first type argument of the Function, you are providing to onResultOf is the type argument of the result.

That also means, that Value is not forced to be a subtype of Map.Entry.

Seelenvirtuose
  • 20,273
  • 6
  • 37
  • 66