Visitar URL original
Slow performance of `contains` / `containsAll` with large `HashSet` instances · Issue #4233 · assertj/assertj · GitHub
Skip to content

Slow performance of contains / containsAll with large HashSet instances #4233

Description

@scordio

Hi all,
I stumbled across this issue when I noticed slow assertThat(actualHashSet).contains(expectedValue) calls on bigger HashSets. Even more surprisingly I noticed that containsAll calls are slow too:

package org.assertj.tests.core.perf;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class ContainsWithAsPerfTest {
  @ParameterizedTest
  @CsvSource({
      "100000, 50000, true, true", // passes, <100ms
      "100000, 50000, true, false", // timeout
      "100000, 50000, false, true", // passes, <100ms
      "100000, 50000, false, false", // timeout
  })
  @Timeout(value = 10, unit = TimeUnit.SECONDS)
  void contains_on_large_hashset_with_as_description_should_be_fast(int actualSize, int expectedSize, boolean withContainsAll, boolean withNativeCheck) {
    HashSet<Integer> actualSet = new HashSet<>(actualSize);
    for (int i = 0; i < actualSize; i++) {
      actualSet.add(i);
    }

    Set<Integer> expectedSet = new HashSet<>();

    for (int i = 0; i < expectedSize; i++) {
      expectedSet.add(i);
    }

    if (withContainsAll)  {
      if  (withNativeCheck) {
        assertThat(actualSet.containsAll(expectedSet)).isTrue();
      } else {
        assertThat(actualSet).containsAll(expectedSet);
      }
    } else {
      for (Integer expectedValue: expectedSet) {

        if (withNativeCheck) {
          assertThat(actualSet.contains(expectedValue)).isTrue();
        } else {
          assertThat(actualSet).contains(expectedValue);
        }
      }
    }
  }
}

Here is a flamegraph from the last run (HashSetAssert.contains):

As you can see it comes from the linear StandardComparisonStrategy.iterableContains. Suppose I cannot reduce the HashSet what is the recommended API to use for this? Is it possible to skip the redundant super.iterableContains check?

Originally posted by @schiemon in #1104

Activity

  1. added theissue type on Apr 29, 2026
  2. mat3e commented on Apr 30, 2026

    @mat3e
    Contributor

    InHashSetComparisonStrategy runs HashSet#contains twice for iterableContains - directly, and through areEqual.

    @Override
    public boolean iterableContains(Iterable<?> iterable, Object value) {
    return originalSet.contains(value) && super.iterableContains(iterable, value);
    }
    @Override
    public boolean areEqual(Object actual, Object other) {
    return originalSet.contains(actual) && super.areEqual(actual, other);
    }

    It should early return when element is not in Set, but for cases like in the example - (un)happy path of all 50k elements matching - it doesn't help.

    We might remove an overriden iterableContains from InHashSetComparisonStrategy, relying just on areEqual, but not sure if this is the way to go 🤔

  3. added a commit that references this issue on Oct 15, 2026
    4c3293d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions