Skip to content

Commit d28facb

Browse files
chaorenGoogle Java Core Libraries
authored and
Google Java Core Libraries
committed
Optimize SetView#equals() to avoid unnecessary iterations. Fixes #7716.
RELNOTES=n/a PiperOrigin-RevId: 735878611
1 parent 9eb72d3 commit d28facb

File tree

4 files changed

+624
-38
lines changed

4 files changed

+624
-38
lines changed

android/guava-tests/test/com/google/common/collect/SetViewTest.java

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.google.common.collect;
1818

19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkState;
21+
import static com.google.common.collect.Iterators.emptyIterator;
1922
import static com.google.common.collect.Sets.difference;
2023
import static com.google.common.collect.Sets.intersection;
2124
import static com.google.common.collect.Sets.newHashSet;
@@ -35,12 +38,16 @@
3538
import com.google.common.collect.testing.TestStringSetGenerator;
3639
import com.google.common.collect.testing.features.CollectionFeature;
3740
import com.google.common.collect.testing.features.CollectionSize;
41+
import com.google.common.testing.EqualsTester;
42+
import java.util.AbstractSet;
3843
import java.util.HashSet;
44+
import java.util.Iterator;
3945
import java.util.Set;
4046
import junit.framework.Test;
4147
import junit.framework.TestCase;
4248
import junit.framework.TestSuite;
4349
import org.jspecify.annotations.NullMarked;
50+
import org.jspecify.annotations.Nullable;
4451

4552
/**
4653
* Unit tests for {@link SetView}s: {@link Sets#union}, {@link Sets#intersection}, {@link
@@ -539,4 +546,197 @@ public void testCopyInto_nonEmptySet() {
539546
assertThat(symmetricDifference(newHashSet(1, 2), newHashSet(2, 3)).copyInto(newHashSet(0, 1)))
540547
.containsExactly(0, 1, 3);
541548
}
549+
550+
public void testUnion_minSize() {
551+
assertMinSize(union(emptySet(), emptySet()), 0);
552+
assertMinSize(union(setSize(2), setSize(3)), 3);
553+
assertMinSize(union(setSize(3), setSize(2)), 3);
554+
assertMinSize(union(setSizeRange(10, 20), setSizeRange(11, 12)), 11);
555+
assertMinSize(union(setSizeRange(11, 12), setSizeRange(10, 20)), 11);
556+
}
557+
558+
public void testUnion_maxSize() {
559+
assertMaxSize(union(emptySet(), emptySet()), 0);
560+
assertMaxSize(union(setSize(2), setSize(3)), 5);
561+
assertMaxSize(union(setSize(3), setSize(2)), 5);
562+
assertMaxSize(union(setSizeRange(10, 20), setSizeRange(11, 12)), 32);
563+
assertMaxSize(union(setSizeRange(11, 12), setSizeRange(10, 20)), 32);
564+
}
565+
566+
public void testUnion_maxSize_saturated() {
567+
assertThat(union(setSize(Integer.MAX_VALUE), setSize(1)).maxSize())
568+
.isEqualTo(Integer.MAX_VALUE);
569+
assertThat(union(setSize(1), setSize(Integer.MAX_VALUE)).maxSize())
570+
.isEqualTo(Integer.MAX_VALUE);
571+
}
572+
573+
public void testIntersection_minSize() {
574+
assertMinSize(intersection(emptySet(), emptySet()), 0);
575+
assertMinSize(intersection(setSize(2), setSize(3)), 0);
576+
assertMinSize(intersection(setSize(3), setSize(2)), 0);
577+
assertMinSize(intersection(setSizeRange(10, 20), setSizeRange(11, 12)), 0);
578+
assertMinSize(intersection(setSizeRange(11, 12), setSizeRange(10, 20)), 0);
579+
}
580+
581+
public void testIntersection_maxSize() {
582+
assertMaxSize(intersection(emptySet(), emptySet()), 0);
583+
assertMaxSize(intersection(setSize(2), setSize(3)), 2);
584+
assertMaxSize(intersection(setSize(3), setSize(2)), 2);
585+
assertMaxSize(intersection(setSizeRange(10, 20), setSizeRange(11, 12)), 12);
586+
assertMaxSize(intersection(setSizeRange(11, 12), setSizeRange(10, 20)), 12);
587+
}
588+
589+
public void testDifference_minSize() {
590+
assertMinSize(difference(emptySet(), emptySet()), 0);
591+
assertMinSize(difference(setSize(2), setSize(3)), 0);
592+
assertMinSize(difference(setSize(3), setSize(2)), 1);
593+
assertMinSize(difference(setSizeRange(10, 20), setSizeRange(1, 2)), 8);
594+
assertMinSize(difference(setSizeRange(1, 2), setSizeRange(10, 20)), 0);
595+
assertMinSize(difference(setSizeRange(10, 20), setSizeRange(11, 12)), 0);
596+
assertMinSize(difference(setSizeRange(11, 12), setSizeRange(10, 20)), 0);
597+
}
598+
599+
public void testDifference_maxSize() {
600+
assertMaxSize(difference(emptySet(), emptySet()), 0);
601+
assertMaxSize(difference(setSize(2), setSize(3)), 2);
602+
assertMaxSize(difference(setSize(3), setSize(2)), 3);
603+
assertMaxSize(difference(setSizeRange(10, 20), setSizeRange(1, 2)), 20);
604+
assertMaxSize(difference(setSizeRange(1, 2), setSizeRange(10, 20)), 2);
605+
assertMaxSize(difference(setSizeRange(10, 20), setSizeRange(11, 12)), 20);
606+
assertMaxSize(difference(setSizeRange(11, 12), setSizeRange(10, 20)), 12);
607+
}
608+
609+
public void testSymmetricDifference_minSize() {
610+
assertMinSize(symmetricDifference(emptySet(), emptySet()), 0);
611+
assertMinSize(symmetricDifference(setSize(2), setSize(3)), 1);
612+
assertMinSize(symmetricDifference(setSize(3), setSize(2)), 1);
613+
assertMinSize(symmetricDifference(setSizeRange(10, 20), setSizeRange(1, 2)), 8);
614+
assertMinSize(symmetricDifference(setSizeRange(1, 2), setSizeRange(10, 20)), 8);
615+
assertMinSize(symmetricDifference(setSizeRange(10, 20), setSizeRange(11, 12)), 0);
616+
assertMinSize(symmetricDifference(setSizeRange(11, 12), setSizeRange(10, 20)), 0);
617+
}
618+
619+
public void testSymmetricDifference_maxSize() {
620+
assertMaxSize(symmetricDifference(emptySet(), emptySet()), 0);
621+
assertMaxSize(symmetricDifference(setSize(2), setSize(3)), 5);
622+
assertMaxSize(symmetricDifference(setSize(3), setSize(2)), 5);
623+
assertMaxSize(symmetricDifference(setSizeRange(10, 20), setSizeRange(1, 2)), 22);
624+
assertMaxSize(symmetricDifference(setSizeRange(1, 2), setSizeRange(10, 20)), 22);
625+
assertMaxSize(symmetricDifference(setSizeRange(10, 20), setSizeRange(11, 12)), 32);
626+
assertMaxSize(symmetricDifference(setSizeRange(11, 12), setSizeRange(10, 20)), 32);
627+
}
628+
629+
public void testSymmetricDifference_maxSize_saturated() {
630+
assertThat(symmetricDifference(setSize(Integer.MAX_VALUE), setSize(1)).maxSize())
631+
.isEqualTo(Integer.MAX_VALUE);
632+
assertThat(symmetricDifference(setSize(1), setSize(Integer.MAX_VALUE)).maxSize())
633+
.isEqualTo(Integer.MAX_VALUE);
634+
}
635+
636+
public void testEquals() {
637+
new EqualsTester()
638+
.addEqualityGroup(
639+
emptySet(),
640+
union(emptySet(), emptySet()),
641+
intersection(newHashSet(1, 2), newHashSet(3, 4)),
642+
difference(newHashSet(1, 2), newHashSet(1, 2)),
643+
symmetricDifference(newHashSet(1, 2), newHashSet(1, 2)))
644+
.addEqualityGroup(
645+
singleton(1),
646+
union(singleton(1), singleton(1)),
647+
intersection(newHashSet(1, 2), newHashSet(1, 3)),
648+
difference(newHashSet(1, 2), newHashSet(2, 3)),
649+
symmetricDifference(newHashSet(1, 2, 3), newHashSet(2, 3)))
650+
.addEqualityGroup(
651+
singleton(2),
652+
union(singleton(2), singleton(2)),
653+
intersection(newHashSet(1, 2), newHashSet(2, 3)),
654+
difference(newHashSet(1, 2), newHashSet(1, 3)),
655+
symmetricDifference(newHashSet(1, 2, 3), newHashSet(1, 3)))
656+
.addEqualityGroup(
657+
newHashSet(1, 2),
658+
union(singleton(1), singleton(2)),
659+
intersection(newHashSet(1, 2), newHashSet(1, 2, 3)),
660+
difference(newHashSet(1, 2, 3), newHashSet(3)),
661+
symmetricDifference(newHashSet(1, 3), newHashSet(2, 3)))
662+
.addEqualityGroup(
663+
newHashSet(3, 2),
664+
union(singleton(3), singleton(2)),
665+
intersection(newHashSet(3, 2), newHashSet(3, 2, 1)),
666+
difference(newHashSet(3, 2, 1), newHashSet(1)),
667+
symmetricDifference(newHashSet(3, 1), newHashSet(2, 1)))
668+
.addEqualityGroup(
669+
newHashSet(1, 2, 3),
670+
union(newHashSet(1, 2), newHashSet(2, 3)),
671+
intersection(newHashSet(1, 2, 3), newHashSet(1, 2, 3)),
672+
difference(newHashSet(1, 2, 3), emptySet()),
673+
symmetricDifference(emptySet(), newHashSet(1, 2, 3)))
674+
.testEquals();
675+
}
676+
677+
public void testEquals_otherSetContainsThrows() {
678+
new EqualsTester()
679+
.addEqualityGroup(new SetContainsThrows())
680+
.addEqualityGroup(intersection(singleton(null), singleton(null))) // NPE
681+
.addEqualityGroup(intersection(singleton(0), singleton(0))) // CCE
682+
.testEquals();
683+
}
684+
685+
/** Returns a {@link Set} with a {@link Set#size()} of {@code size}. */
686+
private static ContiguousSet<Integer> setSize(int size) {
687+
checkArgument(size >= 0);
688+
ContiguousSet<Integer> set = ContiguousSet.closedOpen(0, size);
689+
checkState(set.size() == size);
690+
return set;
691+
}
692+
693+
/**
694+
* Returns a {@link SetView} with a {@link SetView#minSize()} of {@code min} and a {@link
695+
* SetView#maxSize()} of {@code max}.
696+
*/
697+
private static SetView<Integer> setSizeRange(int min, int max) {
698+
checkArgument(min >= 0 && max >= min);
699+
SetView<Integer> set = difference(setSize(max), setSize(max - min));
700+
checkState(set.minSize() == min && set.maxSize() == max);
701+
return set;
702+
}
703+
704+
/**
705+
* Asserts that {@code code} has a {@link SetView#minSize()} of {@code min} and a {@link
706+
* Set#size()} of at least {@code min}.
707+
*/
708+
private static void assertMinSize(SetView<?> set, int min) {
709+
assertThat(set.minSize()).isEqualTo(min);
710+
assertThat(set.size()).isAtLeast(min);
711+
}
712+
713+
/**
714+
* Asserts that {@code code} has a {@link SetView#maxSize()} of {@code max} and a {@link
715+
* Set#size()} of at most {@code max}.
716+
*/
717+
private static void assertMaxSize(SetView<?> set, int max) {
718+
assertThat(set.maxSize()).isEqualTo(max);
719+
assertThat(set.size()).isAtMost(max);
720+
}
721+
722+
/**
723+
* A {@link Set} that throws {@link NullPointerException} and {@link ClassCastException} from
724+
* {@link #contains}.
725+
*/
726+
private static final class SetContainsThrows extends AbstractSet<Void> {
727+
@Override
728+
public boolean contains(@Nullable Object o) {
729+
throw o == null ? new NullPointerException() : new ClassCastException();
730+
}
731+
732+
@Override
733+
public int size() {
734+
return 0;
735+
}
736+
737+
@Override
738+
public Iterator<Void> iterator() {
739+
return emptyIterator();
740+
}
741+
}
542742
}

0 commit comments

Comments
 (0)