Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6576056

(coll) AbstractSet/AbstractMap equals() method is asymmetric with large sets/maps

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P4
    • None
    • 6
    • core-libs

    Description

      FULL PRODUCT VERSION :
      java version "1.6.0_01"
      Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
      Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)


      EXTRA RELEVANT SYSTEM CONFIGURATION :
      None...platform-independent problem

      A DESCRIPTION OF THE PROBLEM :
      The equals() method is flawed for extremely large sets (> Integer.MAX_VALUE elements).

      The size() contract for Collections is this:

          /**
           * Returns the number of elements in this collection. If this collection
           * contains more than <tt>Integer.MAX_VALUE </tt> elements, returns
           * <tt>Integer.MAX_VALUE</tt>.
           *
           * @return the number of elements in this collection
           */
          int size();

      but the AbstractSet equals method does this:

          public boolean equals(Object o) {
          if (o == this)
              return true;

          if (!(o instanceof Set))
              return false;
          Collection c = (Collection) o;
          if (c.size() != size())
              return false;
              try {
                  return containsAll(c);
              } catch (ClassCastException unused) {
                  return false;
              } catch (NullPointerException unused) {
                  return false;
              }
          }

      If I have two Sets, each with at least MAX_VALUE elements, and one is a proper superset of the other, then equals is asymmetric.

      So, if:
          set1 = {0, .., MAX_INT} and set2 = {1, ..., MAX_INT}
      then:
          set1.equals(set2)
      but
         !set2.equals(set1)

      Here's a fix:

        public boolean equals(Object o) {
          if (o == this)
            return true;

          if (!(o instanceof Set)) {
            return false;
          }
          @SuppressWarnings("unchecked") // o instanceof Set ==> o is a Collection
          Collection<?> c = (Collection) o;
          int cSize = c.size();
          if (cSize != size()) {
            return false;
          }
          try {
            boolean result = containsAll(c);
            if (cSize == Integer.MAX_VALUE) {
              result &= c.containsAll(this);
            }
            return result;
          } catch (ClassCastException unused) {
            return false;
          } catch (NullPointerException unused) {
            return false;
          }
        }

      It appears that AbstractMap has the same problem.

      Just to head off questions like "What are you doing, creating sets with more than MAX_INT elements!?!!?" ...I'm creating sets that generate their elements dynamically, so I'm not really allocating MAX_INT objects. :-)

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See description.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      I expect equals() to always be symmetric.
      ACTUAL -
      equals() is asymmetric under the conditions as per the Description field, above.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      No error message...just incorrect behaviour.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      CUSTOMER SUBMITTED WORKAROUND :
      Subclasses of AbstractSet / AbstractMap can override equals() with the fix given in the Description.

      Attachments

        Activity

          People

            Unassigned Unassigned
            ndcosta Nelson Dcosta (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Imported:
              Indexed: