@@ -10794,9 +10794,13 @@ public static <T> SortedSet<T> minus(SortedSet<T> self, Object removeMe) {
1079410794 }
1079510795
1079610796 /**
10797- * Create a List composed of the elements of the first list minus
10797+ * Create a List composed of the elements of the given List minus
1079810798 * every occurrence of elements of the given Collection.
10799- * <pre class="groovyTestCase">assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]</pre>
10799+ * <pre class="groovyTestCase">
10800+ * def one = [1, "a", true, true, false, 5.3, null], two = [null, true, 5.3]
10801+ * def sub = one.asUnmodifiable() - two.asUnmodifiable()
10802+ * assert sub == [1, "a", false]
10803+ * </pre>
1080010804 *
1080110805 * @param self a List
1080210806 * @param removeMe the elements to exclude
@@ -10875,7 +10879,9 @@ public static <T> Collection<T> minus(Iterable<T> self, Iterable<?> removeMe, @C
1087510879 * Create a new Collection composed of the elements of the first Iterable minus
1087610880 * every matching occurrence as determined by the condition comparator of elements of the given Iterable.
1087710881 * <pre class="groovyTestCase">
10878- * assert ['a', 'B', 'c', 'D', 'E'].minus(['b', 'C', 'D'], {@code (i, j) -> i.toLowerCase() <=> j.toLowerCase()}) == ['a', 'E']
10882+ * List<String> one = ['a', 'B', 'c', 'D', 'E'], two = ['b', 'C', 'D']
10883+ * def sub = one.minus(two, Comparator.comparing(String::toLowerCase))
10884+ * assert sub == ['a', 'E']
1087910885 * </pre>
1088010886 *
1088110887 * @param self an Iterable
@@ -10884,48 +10890,45 @@ public static <T> Collection<T> minus(Iterable<T> self, Iterable<?> removeMe, @C
1088410890 * @return a new Collection
1088510891 * @since 4.0.0
1088610892 */
10887- @SuppressWarnings("unchecked")
1088810893 public static <T> Collection<T> minus(Iterable<T> self, Iterable<?> removeMe, Comparator<? super T> comparator) {
10889- Collection<T> ansCollection = createSimilarCollection(self);
10890- if (!self.iterator().hasNext())
10891- return ansCollection;
10892- T head = self.iterator().next();
10893-
10894- // We can't use the same tactic as for intersection
10895- // since AbstractCollection only does a remove on the first
10896- // element it encounters.
10897- boolean nlgnSort = sameType(new Iterable[]{self, removeMe});
10898-
10899- if (nlgnSort && (head instanceof Comparable)) {
10900- //n*LOG(n) version
10901- Set<T> removeMe2 = new TreeSet<>(comparator);
10902- for(Object o: removeMe) {
10903- removeMe2.add((T) o);
10904- }
10905- for (T o : self) {
10906- if (!removeMe2.contains(o))
10907- ansCollection.add(o);
10908- }
10909- } else {
10910- //n*n version
10911- Collection<T> tmpAnswer = asCollection(self);
10912- for (Iterator<T> iter = self.iterator(); iter.hasNext();) {
10913- T element = iter.next();
10914- boolean elementRemoved = false;
10915- for (Iterator<?> iterator = removeMe.iterator(); iterator.hasNext() && !elementRemoved;) {
10916- Object elt = iterator.next();
10917- if (DefaultTypeTransformation.compareEqual(element, elt)) {
10918- iter.remove();
10919- elementRemoved = true;
10920- }
10894+ Collection<T> answer = createSimilarCollection(self);
10895+ Iterator<T> iterator = self.iterator();
10896+ if (iterator.hasNext()) {
10897+ T next = iterator.next();
10898+ boolean more = iterator.hasNext();
10899+ Predicate exclude; // the elements of self are discarded if this returns true
10900+
10901+ // We can't use the same tactic as for intersection, since AbstractCollection
10902+ // only does a remove on the first element it encounters.
10903+ if (next instanceof Comparable && sameType(new Iterable[]{self, removeMe})) {
10904+ // O(log(n)) version
10905+ Set removeMe2 = new TreeSet<>(comparator);
10906+ for (Object o : removeMe) {
10907+ removeMe2.add(o);
1092110908 }
10909+ exclude = removeMe2::contains;
10910+ } else {
10911+ // O(n) version
10912+ exclude = (o1) -> {
10913+ for (Object o2 : removeMe) {
10914+ if (DefaultTypeTransformation.compareEqual(o1, o2)) {
10915+ return true;
10916+ }
10917+ }
10918+ return false;
10919+ };
1092210920 }
1092310921
10924- //remove duplicates
10925- //can't use treeset since the base classes are different
10926- ansCollection.addAll(tmpAnswer);
10922+ while (true) {
10923+ if (!exclude.test(next))
10924+ answer.add(next); // include duplicates unless answer dedups
10925+ if (!more) break; else {
10926+ next = iterator.next();
10927+ more = iterator.hasNext();
10928+ }
10929+ }
1092710930 }
10928- return ansCollection ;
10931+ return answer ;
1092910932 }
1093010933
1093110934 /**
0 commit comments