Index: lucene/core/src/java/org/apache/lucene/util/SentinelIntSet.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP <+>package org.apache.lucene.util;\n\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport java.util.Arrays;\n\n/**\n * A native int set where one value is reserved to mean \"EMPTY\"\n *\n * @lucene.internal\n */\npublic class SentinelIntSet {\n public int[] keys;\n public int count;\n public final int emptyVal;\n public int rehashCount; // the count at which a rehash should be done\n\n /**\n *\n * @param size The minimum number of elements this set should be able to hold without re-hashing (i.e. the slots are guaranteed not to change)\n * @param emptyVal The integer value to use for EMPTY\n */\n public SentinelIntSet(int size, int emptyVal) {\n this.emptyVal = emptyVal;\n int tsize = Math.max(org.apache.lucene.util.BitUtil.nextHighestPowerOfTwo(size), 1);\n rehashCount = tsize - (tsize>>2);\n if (size >= rehashCount) { // should be able to hold \"size\" w/o rehashing\n tsize <<= 1;\n rehashCount = tsize - (tsize>>2);\n }\n keys = new int[tsize];\n if (emptyVal != 0)\n clear();\n }\n\n public void clear() {\n Arrays.fill(keys, emptyVal);\n count = 0;\n }\n\n public int hash(int key) {\n return key;\n }\n\n public int size() { return count; }\n\n /** returns the slot for this key */\n public int getSlot(int key) {\n assert key != emptyVal;\n int h = hash(key);\n int s = h & (keys.length-1);\n if (keys[s] == key || keys[s]== emptyVal) return s;\n\n int increment = (h>>7)|1;\n do {\n s = (s + increment) & (keys.length-1);\n } while (keys[s] != key && keys[s] != emptyVal);\n return s;\n }\n\n /** returns the slot for this key, or -slot-1 if not found */\n public int find(int key) {\n assert key != emptyVal;\n int h = hash(key);\n int s = h & (keys.length-1);\n if (keys[s] == key) return s;\n if (keys[s] == emptyVal) return -s-1;\n\n int increment = (h>>7)|1;\n for(;;) {\n s = (s + increment) & (keys.length-1);\n if (keys[s] == key) return s;\n if (keys[s] == emptyVal) return -s-1;\n }\n }\n\n public boolean exists(int key) {\n return find(key) >= 0;\n }\n\n public int put(int key) {\n int s = find(key);\n if (s < 0) {\n count++;\n if (count >= rehashCount) {\n rehash();\n s = getSlot(key);\n } else {\n s = -s-1;\n }\n keys[s] = key;\n }\n return s;\n }\n\n public void rehash() {\n int newSize = keys.length << 1;\n int[] oldKeys = keys;\n keys = new int[newSize];\n if (emptyVal != 0) Arrays.fill(keys, emptyVal);\n\n for (int i=0; i>2);\n }\n}\n =================================================================== --- lucene/core/src/java/org/apache/lucene/util/SentinelIntSet.java (revision 6cb776eedcea79377b815e557571404675995252) +++ lucene/core/src/java/org/apache/lucene/util/SentinelIntSet.java (revision ) @@ -18,6 +18,7 @@ */ import java.util.Arrays; +import java.util.NoSuchElementException; /** * A native int set where one value is reserved to mean "EMPTY" @@ -121,5 +122,54 @@ keys[newSlot] = key; } rehashCount = newSize - (newSize>>2); + } + + /** Iterate over the int's in this set. */ + public IntIterator values() { + return new IntIterator(keys, emptyVal); + } + + /** Consistent with {@link java.util.Iterator} but returns primitive int's. */ + public class IntIterator { + + //local references for faster access + private final int[] keys; + private final int emptyVal; + + private int idx = -1; + private int idxNext = -1; + + public IntIterator(int[] keys, int emptyVal) { + this.keys = keys; + this.emptyVal = emptyVal; + } + + boolean hasNext() { + if (idxNext == idx) { + //advance idNext + for (idxNext++; idxNext < keys.length; idxNext++) { + if (keys[idxNext] != emptyVal) + break; + } + } + return idxNext < keys.length; + } + + int next() { + if (!hasNext())//establishes idxNext ahead of idx + throw new NoSuchElementException(); + idx = idxNext; + return keys[idx]; + } + + void remove() { + if (idx == -1)//haven't called next() yet + throw new IllegalStateException(); + int v = keys[idx]; + if (v == emptyVal)//remove() called twice + throw new IllegalStateException(); + keys[idx] = emptyVal; + count--; + } } } Index: lucene/core/src/test/org/apache/lucene/util/TestSentinelIntSet.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP <+>package org.apache.lucene.util;\n\n\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.junit.Test;\n\nimport java.util.HashSet;\n\n/**\n *\n *\n **/\npublic class TestSentinelIntSet extends LuceneTestCase {\n\n @Test\n public void test() throws Exception {\n SentinelIntSet set = new SentinelIntSet(10, -1);\n assertFalse(set.exists(50));\n set.put(50);\n assertTrue(set.exists(50));\n assertEquals(1, set.size());\n assertEquals(-11, set.find(10));\n assertEquals(1, set.size());\n set.clear();\n assertEquals(0, set.size());\n assertEquals(50, set.hash(50));\n //force a rehash\n for (int i = 0; i < 20; i++){\n set.put(i);\n }\n assertEquals(20, set.size());\n assertEquals(24, set.rehashCount);\n }\n \n\n @Test\n public void testRandom() throws Exception {\n for (int i=0; i<10000; i++) {\n int initSz = random().nextInt(20);\n int num = random().nextInt(30);\n int maxVal = (random().nextBoolean() ? random().nextInt(50) : random().nextInt(Integer.MAX_VALUE)) + 1;\n\n HashSet a = new HashSet(initSz);\n SentinelIntSet b = new SentinelIntSet(initSz, -1);\n \n for (int j=0; j=0);\n b.put(val);\n \n assertEquals(a.size(), b.size());\n }\n \n }\n\n }\n \n}\n =================================================================== --- lucene/core/src/test/org/apache/lucene/util/TestSentinelIntSet.java (revision 6cb776eedcea79377b815e557571404675995252) +++ lucene/core/src/test/org/apache/lucene/util/TestSentinelIntSet.java (revision ) @@ -70,7 +70,14 @@ assertEquals(a.size(), b.size()); } - + + //test iterator + SentinelIntSet.IntIterator intIterator = b.values(); + while (intIterator.hasNext()) { + assertTrue(a.remove(intIterator.next())); + } + assertTrue(a.isEmpty()); + } }