--- old/src/share/classes/java/util/Collections.java 2010-12-17 16:13:43.000000000 -0800 +++ new/src/share/classes/java/util/Collections.java 2010-12-17 16:13:43.000000000 -0800 @@ -3714,45 +3714,85 @@ } /** - * Returns true if the two specified collections have no + * Returns {@code true} if the two specified collections have no * elements in common. * *

Care must be exercised if this method is used on collections that - * do not comply with the general contract for Collection. + * do not comply with the general contract for {@code Collection}. * Implementations may elect to iterate over either collection and test * for containment in the other collection (or to perform any equivalent * computation). If either collection uses a nonstandard equality test - * (as does a {@link SortedSet} whose ordering is not compatible with - * equals, or the key set of an {@link IdentityHashMap}), both + * (as does a {@link SortedSet} whose ordering is not compatible with + * equals, or the key set of an {@link IdentityHashMap}), both * collections must use the same nonstandard equality test, or the * result of this method is undefined. * *

Note that it is permissible to pass the same collection in both - * parameters, in which case the method will return true if and only if - * the collection is empty. + * parameters, in which case the method will return {@code true} if and + * only if the collection is empty. * * @param c1 a collection * @param c2 a collection - * @throws NullPointerException if either collection is null + * @return {@code true} if the two specified collections have no + * elements in common. + * @throws NullPointerException if either collection is {@code null} * @since 1.5 */ - public static boolean disjoint(Collection c1, Collection c2) { - /* - * We're going to iterate through c1 and test for inclusion in c2. - * If c1 is a Set and c2 isn't, swap the collections. Otherwise, - * place the shorter collection in c1. Hopefully this heuristic - * will minimize the cost of the operation. - */ - if ((c1 instanceof Set) && !(c2 instanceof Set) || - (c1.size() > c2.size())) { - Collection tmp = c1; - c1 = c2; - c2 = tmp; + public static boolean disjoint(Collection c1, Collection c2) { + final boolean c1isSet = c1 instanceof Set; + final Collection contains; + final Collection iterate; + + if(c1isSet ^ (c2 instanceof Set)) { + // one is a Set, one isn't. + + if(c1.isEmpty() || c2.isEmpty()) { + // One of the collections is empty. Nothing will match. + return true; + } + + // Iterate on the one that isn't the Set. + if(c1isSet) { + // c1 has fast contains() + contains = c1; + iterate = c2; + } else { + // c2 has fast contains() + contains = c2; + iterate = c1; + } + } else { + // Neither or both are Sets + int c1Size = c1.size(); + int c2Size = c2.size(); + + if((0 == c1Size) || (0 == c2Size)) { + // One of the collections is empty. Nothing will match. + return true; + } + + // iterate over smaller collection. + // If c1 contains 3 elements and c2 contains 50 elements and + // assuming contains() requires ceiling(n/2) comparisons then + // checking for all c1 elements in c2 would require 75 comparisons + // vs. all c2 elements in c1 would require 100. + if(c1Size < c2Size) { + contains = c2; + iterate = c1; + } else { + contains = c1; + iterate = c2; + } } - for (Object e : c1) - if (c2.contains(e)) + for (Object e : iterate) { + if (contains.contains(e)) { + // found a common element. Collections are not disjoint. return false; + } + } + + // no common elements were found. return true; }