The kotlin.collections package offers many functions to interact with collections. In particular, functions
find(predicate), findLast(predicate), firstOrNull(predicate) and "`lastOrNull(predicate)` return the element
that matches the given predicate.
The functions find(predicate), findLast(predicate), firstOrNull(predicate) and "`lastOrNull(predicate)` can
be improperly used to check the presence of an element that matches the given predicate. In such cases the code is more difficult to read and
understand than it would be with the functions any(predicate), none(predicate) or contains(element).
The pattern of using find(predicate), findLast(predicate), firstOrNull(predicate) and
"`lastOrNull(predicate)` combined with a null check, to check the presence of an element is not immediately clear to readers. For example, the
expression list.find { it > 5 } != null is more difficult to understand than list.any { it > 5 }. The additional
comparison operator increases the complexity of the expression and introduces confusion about the intent of the code.
Replace the use of find(predicate), findLast(predicate), firstOrNull(predicate) and "`lastOrNull(predicate)`
with any(predicate), none(predicate) or contains(element).
There are four possible scenarios:
null:
find { it == element } != null with contains(element). findLast { it == element } != null with contains(element). firstOrNull { x → x == element } != null with contains(element). lastOrNull { x → x == element } != null with contains(element). null:
find { it == element } == null with !contains(element). findLast { it == element } == null with !contains(element). firstOrNull { x → x == element } == null with !contains(element). lastOrNull { x → x == element } == null with !contains(element). null:
find { it > 5 } != null with any { it > 5 }. findLast { it != 5 } != null with any { it != 5 }. firstOrNull { x → x < 5 } != null with any { x → x < 5 }. lastOrNull { x → x != 5 } != null with any { x → x != 5 }. null:
find { it > 5 } == null with none { it > 5 }. findLast { it != 5 } == null with none { it != 5 }. firstOrNull { x → x < 5 } == null with none { x → x < 5 }. lastOrNull { x → x != 5 } == null with none { x → x != 5 }.
fun example(list: List<Int>) {
list.find { it > 5 } != null // Noncompliant
list.findLast { it > 5 } == null // Noncompliant
list.firstOrNull { it == 5 } != null // Noncompliant
list.lastOrNull { x -> x == 5 } != null // Noncompliant
list.find { x -> 5 == 4 } != null // Noncompliant, note that this case cannot be fixed using contains
}
fun example(list: List<Int>) {
list.any { it > 5 } // Compliant
list.none { it > 5 } // Compliant
list.contains(5) // Compliant
!list.contains(5) // Compliant
list.any { x -> 5 == 4 } // Compliant, note that this case cannot be fixed using contains
}