private static <K, V> boolean equivalent(
      @Nullable HamtPMap<K, V> t1,
      @Nullable HamtPMap<K, V> t2,
      BiPredicate<V, V> equivalence) {
    if (t1 == t2) {
      return true;
    } else if (t1 == null || t2 == null) {
      return false;
    }

    if (t1.hash != t2.hash) {
      // Due to our invariant, we can safely conclude that there's a discrepancy in the
      // keys without any extra work.
      return false;
    } else if (!t1.key.equals(t2.key)) {
      // Hash collision: try to rearrange t2 to have the same root as t1
      t2 = t2.pivot(t1.key, t1.hash);
      if (t2.key == null) {
        // t1.key not found in t2
        return false;
      }
    }

    if (!equivalence.test(t1.value, t2.value)) {
      return false;
    }
    int mask = t1.mask | t2.mask;
    while (mask != 0) {
      int childBit = Integer.lowestOneBit(mask);
      mask &= ~childBit;
      if (!equivalent(t1.getChild(childBit), t2.getChild(childBit), equivalence)) {
        return false;
      }
    }
    return true;
  }
private static < K , V > boolean equivalent ( @ Nullable HamtPMap < K , V > t1 , @ Nullable HamtPMap < K , V > t2 , BiPredicate < V , V > equivalence ) { if ( t1 == t2 ) { return true ; } else if ( t1 == null || t2 == null ) { return false ; } if ( t1 . hash != t2 . hash ) { return false ; } else if ( ! t1 . key . equals ( t2 . key ) ) { t2 = t2 . pivot ( t1 . key , t1 . hash ) ; if ( t2 . key == null ) { return false ; } } if ( ! equivalence . test ( t1 . value , t2 . value ) ) { return false ; } int mask = t1 . mask | t2 . mask ; while ( mask != 0 ) { int childBit = Integer . lowestOneBit ( mask ) ; mask &= ~ childBit ; if ( ! equivalent ( t1 . getChild ( childBit ) , t2 . getChild ( childBit ) , equivalence ) ) { return false ; } } return true ; }
Internal recursive implementation of equivalent(HamtPMap, BiPredicate).