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.

Why is this an issue?

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).

What is the potential impact?

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.

How to fix it

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:

  1. The predicate is a simple binary expression checking for equality, and the found object is compared to not be null:
  2. The predicate is a simple binary expression checking for equality, and the found object is compared to be null:
  3. The predicate is any binary expression not checking for equality, and the found object is compared to not be null:
  4. The predicate is any binary expression not checking for equality, and the found object is compared to be null:

Code examples

Noncompliant code example

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
}

Compliant solution

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
}

Resources

Documentation