lengthCompare
, Why It's Needed, and Why It Needed to be Fixed
What is lengthCompare
?
It is recommended by experts and some IDEs to replace comparisons to Seq#length
with calls to Seq#lengthCompare
. What is lengthCompare
? From the scaladoc:
For those familiar, this is the same contract used by Java’s Comparator
s and Scala's Ordering
s when comparing two values. If the first value is larger, a positive number is returned; if the second value is larger, a negative number is returned, and if they are equivalent, 0
is returned. Thus, seq.lengthCompare(x)
is equivalent to Integer.compare(seq.length, x)
.
lengthCompare
is Always Efficient
Why do I need lengthCompare
though? I can just use the normal comparison operators, and it's much simpler. seq.length < 10
is much more readable than seq.lengthCompare(10) < 0
.
Unfortunately, Seq#length
can potentially be expensive. If you write seq.length < 10
, you want to know if the Seq
has fewer than ten elements. However, if the Seq
has two million elements, it may have to traverse all two million to compute seq.length
; this is highly inefficient if you only want to compare the length to a small number. Worse still, some Seq
implementations (e.g. LazyList
or its predecessor Stream
) may have infinitely many elements, in which case seq.length
will never finish evaluating.
To solve this problem, lengthCompare
iterates through at most one more element than the length compared against, unless it knows that length
can be computed cheaply (see the scaladoc for the full details).
lengthCompare
Usage is Awkward
The problem with lengthCompare
is, it's ugly, somewhat difficult to read, and understanding code which uses it can require an extra layer of mental gymnastics (especially for beginners). It can take some time for your brain to automatically transform seq.lengthCompare(x) op 0
into seq.length op x
(where op
is <
, >
, etc.), and until then, code which uses lengthCompare
can be quite confusing.
The Solution: lengthIs
To regain readability, we can use a new method: lengthIs
. How do you use it? Exactly as if it was the length
method.
Under the hood, lengthIs
calls lengthCompare
, so it's still very efficient, but it does some work to make the comparisons look natural and intuitive.
If all you wanted was a nicer way to compare Seq
lengths, you can stop here; otherwise, see the Appendix to find out how lengthIs
works.
Conclusion
lengthIs
is a great new way to have readable code while retaining efficient computation. If you need to perform multiple comparisons on the result of lengthCompare
, you can still use it, but in most cases, lengthIs
will be preferable.
Additional Methods: sizeCompare
and sizeIs
Scala 2.13 also adds sizeCompare
and sizeIs
methods, which do the same thing as lengthCompare
and lengthIs
, except they work on Iterable
rather than just Seq
.
Appendix: How Do lengthIs
and sizeIs
Work?
Unlike length
and size
which return Int
s, lengthIs
and sizeIs
actually return a value class called IterableOps.SizeCompareOps
, which defines six methods - the comparison operators (<
, <=
, ==
, !=
, >=
and >
). Each of these methods calls sizeCompare
on the collection, and compares the result to 0
as appropriate.
Additionally, lengthIs
, sizeIs
and IterableOps.SizeCompareOps
are defined carefully to have negligible overhead cost, so even in performance critical code, you can use lengthIs
or sizeIs
and have readable code without worrying about reducing efficiency (you can find the benchmark results here).
References
lengthIs
was added in this pull requestsizeIs
andsizeCompare
were added in this pull request