Avoid List.contains In Loop

ID

java.loop_list_contains

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

Java

Tags

efficiency

Description

Reports calls to contains on a List-shaped receiver (List, ArrayList, LinkedList, Vector, Stack, ArrayDeque) made from inside the body or condition of a loop.

Rationale

List.contains is O(n) — it scans the backing array or linked cells comparing each element with equals. Used inside an outer loop of size m, the combined work becomes O(n·m), and if n and m are both user-sized, the runtime blows up visibly under load. Sets are the natural fix: HashSet.contains is amortised O(1), and building a Set view of the lookup list once — before the loop — turns the hot path back into linear time.

// Bad — allowList.contains runs for every candidate
for (String c : candidates) {
    if (allowList.contains(c)) {
        process(c);
    }
}

// Good — build the lookup set once
Set<String> allow = new HashSet<>(allowList);
for (String c : candidates) {
    if (allow.contains(c)) {
        process(c);
    }
}

Remediation

Wrap the lookup collection in a HashSet (or another Set implementation) before the loop, and use the set for membership tests. When the list is small and fixed (a handful of constant values), also consider Set.of(…​) as a static final field.

References