diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasher.java b/src/main/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasher.java index 56be62b32..f3d10a4cb 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasher.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasher.java @@ -102,9 +102,7 @@ public class DynamicHasher implements Hasher { @Override public boolean hasNext() { - if (buffers.isEmpty()) { - return false; - } + // Note: This iterator is only used when buffers.size() is not zero return buffer < buffers.size() - 1 || funcCount < shape.getNumberOfHashFunctions(); } @@ -123,6 +121,29 @@ public class DynamicHasher implements Hasher { } } + /** + * An iterator of integers to use then there are no values. + */ + private static class NoValuesIterator implements PrimitiveIterator.OfInt { + /** The singleton instance. */ + private static final NoValuesIterator INSTANCE = new NoValuesIterator(); + + /** + * Empty constructor. + */ + private NoValuesIterator() {} + + @Override + public boolean hasNext() { + return false; + } + + @Override + public int nextInt() { + throw new NoSuchElementException(); + } + } + /** * The list of byte arrays that are to be hashed. */ @@ -163,7 +184,8 @@ public class DynamicHasher implements Hasher { HashFunctionIdentity.asCommonString(shape.getHashFunctionIdentity()), HashFunctionIdentity.asCommonString(getHashFunctionIdentity()))); } - return new Iterator(shape); + // Use optimised iterator for no values + return buffers.isEmpty() ? NoValuesIterator.INSTANCE : new Iterator(shape); } @Override diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasherTest.java b/src/test/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasherTest.java index 3babbb75d..862136aeb 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasherTest.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/hasher/DynamicHasherTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.NoSuchElementException; import java.util.PrimitiveIterator.OfInt; import org.apache.commons.collections4.bloomfilter.hasher.function.MD5Cyclic; @@ -108,6 +109,12 @@ public class DynamicHasherTest { assertEquals(element, iter.nextInt()); } assertFalse(iter.hasNext()); + try { + iter.next(); + fail("Should have thown NoSuchElementException"); + } catch (final NoSuchElementException ignore) { + // do nothing + } } /** @@ -127,11 +134,21 @@ public class DynamicHasherTest { } /** - * Tests if isEmpty() reports correctly. + * Tests if isEmpty() reports correctly and the iterator returns no values. */ @Test public void testIsEmpty() { - assertTrue(builder.build().isEmpty()); + DynamicHasher hasher = builder.build(); + assertTrue(hasher.isEmpty()); + final OfInt iter = hasher.getBits(shape); + assertFalse(iter.hasNext()); + try { + iter.next(); + fail("Should have thown NoSuchElementException"); + } catch (final NoSuchElementException expected) { + // do nothing + } + assertFalse(builder.with("Hello").build().isEmpty()); } }