If I have a list-of-lists, then .sort()
works naturally; i.e., element-wise:
groovy:000> [[3,2,1],[3,1,2],[2,1,3],[2,3,1],[1,2,3],[1,3,2],[1,3,3],[1,2,2]].sort()
===> [[1, 2, 2], [1, 2, 3], [1, 3, 2], [1, 3, 3], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
However, if I write an explicit sort closure, using the <=>
operator, it fails:
groovy:000> [[3,2,1],[3,1,2],[2,1,3],[2,3,1],[1,2,3],[1,3,2],[1,3,3],[1,2,2]].sort { a, b -> a <=> b }
ERROR java.lang.IllegalArgumentException:
Cannot compare java.util.ArrayList with value '[3, 1, 2]' and java.util.ArrayList with value '[3, 2, 1]'
at groovysh_evaluate$_run_closure1.doCall (groovysh_evaluate:3)
In this toy case, it doesn't matter, but I have a more complicated structure that requires me to use a sort closure. I understand that <=>
, per the documentation, is syntactic sugar for .compareTo
, which java.util.ArrayList
values don't have. However, how does the "bare sort" get around this problem?
The reason I ask is because I found a solution, but it feels a bit cludgy:
listOfLists.sort { a, b -> new Tuple(*a) <=> new Tuple(*b) }
I wonder if there's a more idiomatic solution that effectively does the same as the bare sort?
<=>
is an overloaded operator for Comparable.compareTo
; therefor the
LHS must implement this method and List
does not, hence the error.
When using just [].sort()
, then groovy uses NumberAwareComparator
,
which tries to use
DefaultTypeTransformation.compareTo
,
which again expects the LHS to be
a Comparable
(or a Number
, but we can ignore this here). Since the LHS is not
a Comparable
, this method throws and NumberAwareComparator
falls
back to comparing the hash
codes
of both sides.
Whether to trust this behaviour is a different story. Wrapping the
comparison in a container, that can compareTo
(like Tuple
) feels way
safer (e.g. it properly short-circuits)