/* * Copyright 2014 Goldman Sachs. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gs.collections.impl.set.mutable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Set; import com.gs.collections.api.list.MutableList; import com.gs.collections.api.set.MutableSet; import com.gs.collections.impl.CollidingInt; import com.gs.collections.impl.block.factory.Procedures; import com.gs.collections.impl.factory.Lists; import com.gs.collections.impl.list.mutable.FastList; import com.gs.collections.impl.test.SerializeTestHelper; import com.gs.collections.impl.test.Verify; import org.junit.Assert; import org.junit.Test; /** * JUnit test suite for {@link UnifiedSet}. */ public class UnifiedSetAcceptanceTest { @Test public void testUnifiedSetWithCollisions() { for (int removeStride = 2; removeStride < 9; removeStride++) { assertUnifiedSetWithCollisions(0, removeStride); assertUnifiedSetWithCollisions(1, removeStride); assertUnifiedSetWithCollisions(2, removeStride); assertUnifiedSetWithCollisions(3, removeStride); assertUnifiedSetWithCollisions(4, removeStride); } } private static void assertUnifiedSetWithCollisions(int shift, int removeStride) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 84000; // divisible by every integer between 2 and 8 for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(new CollidingInt(i, shift))); } Verify.assertSize(size, set); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), set); } for (int i = 0; i < size; i += removeStride) { Assert.assertTrue(set.remove(new CollidingInt(i, shift))); } Verify.assertSize(size - size / removeStride, set); for (int i = 0; i < size; i++) { if (i % removeStride == 0) { Verify.assertNotContains(new CollidingInt(i, shift), set); } else { Verify.assertContains(new CollidingInt(i, shift), set); } } for (int i = 0; i < size; i++) { set.remove(new CollidingInt(i, shift)); } Verify.assertEmpty(set); } @Test public void testUnifiedSetWithCollisionsAndNullKey() { for (int removeStride = 2; removeStride < 9; removeStride++) { setupAndAssertUnifiedSetWithCollisionsAndNullKey(0, removeStride); setupAndAssertUnifiedSetWithCollisionsAndNullKey(1, removeStride); setupAndAssertUnifiedSetWithCollisionsAndNullKey(2, removeStride); setupAndAssertUnifiedSetWithCollisionsAndNullKey(3, removeStride); setupAndAssertUnifiedSetWithCollisionsAndNullKey(4, removeStride); } } private static void setupAndAssertUnifiedSetWithCollisionsAndNullKey(int shift, int removeStride) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); Verify.assertEmpty(set); int size = 84000; for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(new CollidingInt(i, shift))); } set.add(null); UnifiedSet<CollidingInt> clone = set.clone(); assertUnifiedSetWithCollisionsAndNullKey(shift, removeStride, clone, size); assertUnifiedSetWithCollisionsAndNullKey(shift, removeStride, set, size); } private static void assertUnifiedSetWithCollisionsAndNullKey( int shift, int removeStride, UnifiedSet<CollidingInt> set, int size) { Verify.assertSize(size + 1, set); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), set); } Verify.assertContains(null, set); for (int i = 0; i < size; i += removeStride) { Assert.assertTrue(set.remove(new CollidingInt(i, shift))); } Verify.assertSize(size - size / removeStride + 1, set); for (int i = 0; i < size; i++) { if (i % removeStride != 0) { Verify.assertContains(new CollidingInt(i, shift), set); } } Verify.assertContains(null, set); set.remove(null); Verify.assertNotContains(null, set); } @Test public void testUnifiedSet() { UnifiedSet<Integer> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(i)); } Verify.assertSize(size, set); for (int i = 0; i < size; i++) { Verify.assertContains(i, set); } for (int i = 0; i < size; i += 2) { Assert.assertTrue(set.remove(i)); } Verify.assertSize(size / 2, set); for (int i = 1; i < size; i += 2) { Verify.assertContains(i, set); } } @Test public void testUnifiedSetClear() { assertUnifiedSetClear(0); assertUnifiedSetClear(1); assertUnifiedSetClear(2); assertUnifiedSetClear(3); } private static void assertUnifiedSetClear(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(new CollidingInt(i, shift))); } set.clear(); Verify.assertEmpty(set); for (int i = 0; i < size; i++) { Verify.assertNotContains(new CollidingInt(i, shift), set); } } @Test public void testUnifiedSetForEach() { assertUnifiedSetForEach(0); assertUnifiedSetForEach(1); assertUnifiedSetForEach(2); assertUnifiedSetForEach(3); } private static void assertUnifiedSetForEach(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(new CollidingInt(i, shift))); } MutableList<CollidingInt> keys = FastList.newList(size); set.forEach(Procedures.cast(keys::add)); Verify.assertSize(size, keys); Collections.sort(keys); for (int i = 0; i < size; i++) { Verify.assertItemAtIndex(new CollidingInt(i, shift), i, keys); } } @Test public void testUnifiedSetForEachWith() { assertUnifiedSetForEachWith(0); assertUnifiedSetForEachWith(1); assertUnifiedSetForEachWith(2); assertUnifiedSetForEachWith(3); } private static void assertUnifiedSetForEachWith(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(new CollidingInt(i, shift))); } MutableList<CollidingInt> keys = FastList.newList(size); set.forEachWith((key, s) -> { Assert.assertEquals("foo", s); keys.add(key); }, "foo"); Verify.assertSize(size, keys); Collections.sort(keys); for (int i = 0; i < size; i++) { Verify.assertItemAtIndex(new CollidingInt(i, shift), i, keys); } } @Test public void testUnifiedSetForEachWithIndex() { assertUnifiedSetForEachWithIndex(0); assertUnifiedSetForEachWithIndex(1); assertUnifiedSetForEachWithIndex(2); assertUnifiedSetForEachWithIndex(3); } private static void assertUnifiedSetForEachWithIndex(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { Assert.assertTrue(set.add(new CollidingInt(i, shift))); } MutableList<CollidingInt> keys = FastList.newList(size); int[] prevIndex = new int[1]; set.forEachWithIndex((key, index) -> { Assert.assertEquals(prevIndex[0], index); prevIndex[0]++; keys.add(key); }); Verify.assertSize(size, keys); Collections.sort(keys); for (int i = 0; i < size; i++) { Verify.assertItemAtIndex(new CollidingInt(i, shift), i, keys); } } @Test public void testUnifiedSetAddAll() { assertUnifiedSetAddAll(0); assertUnifiedSetAddAll(1); assertUnifiedSetAddAll(2); assertUnifiedSetAddAll(3); } private static void assertUnifiedSetAddAll(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } UnifiedSet<CollidingInt> newSet = UnifiedSet.newSet(size); newSet.addAll(set); Verify.assertSize(size, newSet); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), newSet); } } @Test public void testUnifiedSetAddAllWithHashSet() { assertUnifiedSetAddAllWithHashSet(0); assertUnifiedSetAddAllWithHashSet(1); assertUnifiedSetAddAllWithHashSet(2); assertUnifiedSetAddAllWithHashSet(3); } private static void assertUnifiedSetAddAllWithHashSet(int shift) { Set<CollidingInt> set = new HashSet<>(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } UnifiedSet<CollidingInt> newSet = UnifiedSet.newSet(size); newSet.addAll(set); Verify.assertSize(size, newSet); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), newSet); } UnifiedSet<CollidingInt> newSet2 = UnifiedSet.newSet(); newSet2.addAll(set); Verify.assertSize(size, newSet2); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), newSet2); } } @Test public void testUnifiedSetReplace() { assertUnifiedSetReplace(0); assertUnifiedSetReplace(1); assertUnifiedSetReplace(2); assertUnifiedSetReplace(3); } private static void assertUnifiedSetReplace(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } Verify.assertSize(size, set); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), set); } } @Test public void testUnifiedSetRetainAllFromList() { runUnifiedSetRetainAllFromList(0); runUnifiedSetRetainAllFromList(1); runUnifiedSetRetainAllFromList(2); runUnifiedSetRetainAllFromList(3); } private static void runUnifiedSetRetainAllFromList(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); MutableList<CollidingInt> toRetain = Lists.mutable.of(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); if (i % 2 == 0) { toRetain.add(new CollidingInt(i, shift)); } } Verify.assertSize(size, set); Assert.assertTrue(set.containsAll(toRetain)); Assert.assertTrue(set.retainAll(toRetain)); Assert.assertTrue(set.containsAll(toRetain)); Assert.assertFalse(set.retainAll(toRetain)); // a second call should not modify the set Verify.assertSize(size / 2, set); for (int i = 0; i < size; i += 2) { Verify.assertContains(new CollidingInt(i, shift), set); } } @Test public void testUnifiedSetRetainAllFromSet() { runUnifiedSetRetainAllFromSet(0); runUnifiedSetRetainAllFromSet(1); runUnifiedSetRetainAllFromSet(2); runUnifiedSetRetainAllFromSet(3); } private static void runUnifiedSetRetainAllFromSet(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); Set<CollidingInt> toRetain = new HashSet<>(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); if (i % 2 == 0) { toRetain.add(new CollidingInt(i, shift)); } } Verify.assertSize(size, set); Assert.assertTrue(set.containsAll(toRetain)); Assert.assertTrue(set.retainAll(toRetain)); Assert.assertTrue(set.containsAll(toRetain)); Assert.assertFalse(set.retainAll(toRetain)); // a second call should not modify the set Verify.assertSize(size / 2, set); for (int i = 0; i < size; i += 2) { Verify.assertContains(new CollidingInt(i, shift), set); } } @Test public void testUnifiedSetToArray() { runUnifiedSetToArray(0); runUnifiedSetToArray(1); runUnifiedSetToArray(2); runUnifiedSetToArray(3); } private static void runUnifiedSetToArray(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } Verify.assertSize(size, set); Object[] keys = set.toArray(); Assert.assertEquals(size, keys.length); Arrays.sort(keys); for (int i = 0; i < size; i++) { Verify.assertItemAtIndex(new CollidingInt(i, shift), i, keys); } } @Test public void testUnifiedSetIterator() { runUnifiedSetIterator(0); runUnifiedSetIterator(1); runUnifiedSetIterator(2); runUnifiedSetIterator(3); } private static void runUnifiedSetIterator(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } Verify.assertSize(size, set); CollidingInt[] keys = new CollidingInt[size]; int count = 0; for (Iterator<CollidingInt> it = set.iterator(); it.hasNext(); ) { keys[count++] = it.next(); } Arrays.sort(keys); for (int i = 0; i < size; i++) { Assert.assertEquals(new CollidingInt(i, shift), keys[i]); } } @Test public void testUnifiedSetIteratorRemove() { for (int removeStride = 2; removeStride < 9; removeStride++) { runUnifiedSetIteratorRemove(0, removeStride); runUnifiedSetIteratorRemove(1, removeStride); runUnifiedSetIteratorRemove(2, removeStride); runUnifiedSetIteratorRemove(3, removeStride); } } private static void runUnifiedSetIteratorRemove(int shift, int removeStride) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } Verify.assertSize(size, set); int count = 0; for (Iterator<CollidingInt> it = set.iterator(); it.hasNext(); ) { CollidingInt key = it.next(); count++; if (key.getValue() % removeStride == 0) { it.remove(); } } Assert.assertEquals(size, count); for (int i = 0; i < size; i++) { if (i % removeStride != 0) { Verify.assertContains( "set contains " + i + "for shift " + shift + " and remove stride " + removeStride, new CollidingInt(i, shift), set); } } } @Test public void testUnifiedSetIteratorRemoveFlip() { for (int removeStride = 2; removeStride < 9; removeStride++) { runUnifiedSetKeySetIteratorRemoveFlip(0, removeStride); runUnifiedSetKeySetIteratorRemoveFlip(1, removeStride); runUnifiedSetKeySetIteratorRemoveFlip(2, removeStride); runUnifiedSetKeySetIteratorRemoveFlip(3, removeStride); } } private static void runUnifiedSetKeySetIteratorRemoveFlip(int shift, int removeStride) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } Verify.assertSize(size, set); int count = 0; for (Iterator<CollidingInt> it = set.iterator(); it.hasNext(); ) { CollidingInt key = it.next(); count++; if (key.getValue() % removeStride != 0) { it.remove(); } } Assert.assertEquals(size, count); for (int i = 0; i < size; i++) { if (i % removeStride == 0) { Verify.assertContains( "set contains " + i + "for shift " + shift + " and remove stride " + removeStride, new CollidingInt(i, shift), set); } } } @Test public void testUnifiedSetSerialize() { runUnifiedSetSerialize(0); runUnifiedSetSerialize(1); runUnifiedSetSerialize(2); runUnifiedSetSerialize(3); } private static void runUnifiedSetSerialize(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); } set.add(null); set = SerializeTestHelper.serializeDeserialize(set); Verify.assertSize(size + 1, set); for (int i = 0; i < size; i++) { Verify.assertContains(new CollidingInt(i, shift), set); } Verify.assertContains(null, set); } public static <T> T[] shuffle(T[] array) { Object[] result = new Object[array.length]; Random rand = new Random(12345678912345L); int left = array.length; for (int i = 0; i < array.length; i++) { int chosen = rand.nextInt(left); result[i] = array[chosen]; left--; array[chosen] = array[left]; array[left] = null; } System.arraycopy(result, 0, array, 0, array.length); return array; } @Test public void testUnifiedSetEqualsAndHashCode() { assertUnifiedSetEqualsAndHashCode(0); assertUnifiedSetEqualsAndHashCode(1); assertUnifiedSetEqualsAndHashCode(2); assertUnifiedSetEqualsAndHashCode(3); } private static void assertUnifiedSetEqualsAndHashCode(int shift) { MutableSet<CollidingInt> set1 = UnifiedSet.newSet(); Set<CollidingInt> set2 = new HashSet<>(); MutableSet<CollidingInt> set3 = UnifiedSet.newSet(); MutableSet<CollidingInt> set4 = UnifiedSet.newSet(); int size = 100000; for (int i = 0; i < size; i++) { set1.add(new CollidingInt(i, shift)); set2.add(new CollidingInt(i, shift)); set3.add(new CollidingInt(i, shift)); set4.add(new CollidingInt(size - i - 1, shift)); } Verify.assertSetsEqual(set2, set1); Verify.assertSetsEqual(set1, set2); Verify.assertEqualsAndHashCode(set2, set1); Verify.assertSetsEqual(set1, set3); Verify.assertEqualsAndHashCode(set1, set3); Verify.assertSetsEqual(set2, set4); Verify.assertSetsEqual(set4, set2); Verify.assertEqualsAndHashCode(set2, set4); } @Test public void testUnifiedSetRemoveAll() { runUnifiedSetRemoveAll(0); runUnifiedSetRemoveAll(1); runUnifiedSetRemoveAll(2); runUnifiedSetRemoveAll(3); } private static void runUnifiedSetRemoveAll(int shift) { UnifiedSet<CollidingInt> set = UnifiedSet.newSet(); List<CollidingInt> toRemove = new ArrayList<>(); int size = 100000; for (int i = 0; i < size; i++) { set.add(new CollidingInt(i, shift)); if (i % 2 == 0) { toRemove.add(new CollidingInt(i, shift)); } } Verify.assertSize(size, set); Assert.assertTrue(set.removeAll(toRemove)); Assert.assertFalse(set.removeAll(toRemove)); // a second call should not modify the set Verify.assertSize(size / 2, set); for (int i = 1; i < size; i += 2) { Verify.assertContains(new CollidingInt(i, shift), set); } } @Test public void testUnifiedSetPutDoesNotReplace() { this.assertUnifiedSetPutDoesNotReplace(0); this.assertUnifiedSetPutDoesNotReplace(1); this.assertUnifiedSetPutDoesNotReplace(2); this.assertUnifiedSetPutDoesNotReplace(3); this.assertUnifiedSetPutDoesNotReplace(4); } private void assertUnifiedSetPutDoesNotReplace(int shift) { UnifiedSet<CollidingIntWithFlag> set = UnifiedSet.newSet(); for (int i = 0; i < 1000; i++) { Assert.assertTrue(set.add(new CollidingIntWithFlag(i, shift, false))); } Assert.assertEquals(1000, set.size()); for (int i = 0; i < 1000; i++) { Assert.assertFalse(set.add(new CollidingIntWithFlag(i, shift, true))); } Assert.assertEquals(1000, set.size()); for (CollidingIntWithFlag ciwf : set) { Assert.assertFalse(ciwf.flag); } } @Test public void testUnifiedSetAsPool() { this.runUnifiedSetAsPool(0); this.runUnifiedSetAsPool(1); this.runUnifiedSetAsPool(2); this.runUnifiedSetAsPool(3); } private void runUnifiedSetAsPool(int shift) { CollidingInt[] toPool = new CollidingInt[5000]; UnifiedSet<CollidingInt> set = new UnifiedSet<>(); for (int i = 0; i < toPool.length; i++) { toPool[i] = new CollidingInt(i, shift); Assert.assertSame(toPool[i], set.put(toPool[i])); } for (int i = 0; i < toPool.length; i++) { Assert.assertSame(toPool[i], set.put(new CollidingInt(i, shift))); } Random random = new Random(); for (int i = 0; i < toPool.length * 4; i++) { int x = random.nextInt(toPool.length); Assert.assertSame(toPool[x], set.put(new CollidingInt(x, shift))); } for (int i = 0; i < toPool.length; i++) { Assert.assertSame(toPool[i], set.get(toPool[i])); } for (int i = 0; i < toPool.length * 4; i++) { int x = random.nextInt(toPool.length); Assert.assertSame(toPool[x], set.removeFromPool(new CollidingInt(x, shift))); toPool[x] = null; } } @Test public void testUnifiedSetAsPoolRandomInput() { this.runUnifiedSetAsPoolRandomInput(0); this.runUnifiedSetAsPoolRandomInput(1); this.runUnifiedSetAsPoolRandomInput(2); this.runUnifiedSetAsPoolRandomInput(3); } private void runUnifiedSetAsPoolRandomInput(int shift) { CollidingInt[] toPool = new CollidingInt[5000]; for (int i = 0; i < toPool.length; i++) { toPool[i] = new CollidingInt(i, shift); } toPool = shuffle(toPool); UnifiedSet<CollidingInt> set = new UnifiedSet<>(); for (int i = 0; i < toPool.length; i++) { Assert.assertSame(toPool[i], set.put(toPool[i])); } for (int i = 0; i < toPool.length; i++) { Assert.assertSame(toPool[i], set.put(new CollidingInt(toPool[i].getValue(), shift))); } Random random = new Random(); for (int i = 0; i < toPool.length * 4; i++) { int x = random.nextInt(toPool.length); Assert.assertSame(toPool[x], set.put(new CollidingInt(toPool[x].getValue(), shift))); } for (int i = 0; i < toPool.length; i++) { Assert.assertSame(toPool[i], set.get(toPool[i])); } } private static final class CollidingIntWithFlag extends CollidingInt { private static final long serialVersionUID = 1L; private final boolean flag; private CollidingIntWithFlag(int value, int shift, boolean flag) { super(value, shift); this.flag = flag; } } }