From 530461f9998dc358932de0b2feca44da4123feef Mon Sep 17 00:00:00 2001
Date: Thu, 27 Aug 2026 19:50:31 +0800
Subject: [PATCH] Improve primitive hashset
.../share/classfile/bytecodeEnhancement.cpp | 20 +-
src/java.base/share/legal/fastutil.md | 214 +++
.../PrimitiveHashSetTest.java | 27 +-
.../HashSet/PrimitiveHashSetBehaviorTest.java | 61 +-
.../classdata/java/util/HashSet.java | 1323 +----------------
.../classdata/java/util/IntHashSet.java | 531 +++++++
.../classdata/java/util/LongHashSet.java | 532 +++++++
.../classdata/java/util/PrimitiveHashSet.java | 225 +++
8 files changed, 1658 insertions(+), 1275 deletions(-)
create mode 100644 src/java.base/share/legal/fastutil.md
create mode 100644 test/jdk/jdk/internal/enhancement/classdata/java/util/IntHashSet.java
create mode 100644 test/jdk/jdk/internal/enhancement/classdata/java/util/LongHashSet.java
create mode 100644 test/jdk/jdk/internal/enhancement/classdata/java/util/PrimitiveHashSet.java
@@ -723,16 +723,16 @@ void BytecodeEnhancement::initialize() {
_owner_class_loaders = new (mtClass) BytecodeEnhancementOwnerTable();
if (UsePrimitiveHashSet) {
add_internal_class_replacement("java/util/HashSet", 0x7a6d746bU);
- add_internal_class_addition("java/util/HashSet$PrimitiveHashSet", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$PrimitiveHashSet$1", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$LongHashSet", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$LongHashSet$LongIterator", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$LongHashSet$LongSpliterator", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$LongHashSet$LongArrayList", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$IntHashSet", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$IntHashSet$IntIterator", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$IntHashSet$IntSpliterator", BuiltinLoader::BootLoader);
- add_internal_class_addition("java/util/HashSet$IntHashSet$IntArrayList", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/PrimitiveHashSet", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/PrimitiveHashSet$1", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/LongHashSet", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/LongHashSet$LongIterator", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/LongHashSet$LongSpliterator", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/LongHashSet$LongArrayList", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/IntHashSet", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/IntHashSet$IntIterator", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/IntHashSet$IntSpliterator", BuiltinLoader::BootLoader);
+ add_internal_class_addition("java/util/IntHashSet$IntArrayList", BuiltinLoader::BootLoader);
}
if (BytecodeEnhancementPaths != nullptr) {
GrowableArray<ClassPathEntry*> path_entries;
new file mode 100644
@@ -0,0 +1,214 @@
+## fastutil
+
+### Notice
+This product includes code derived from fastutil OpenHashSet.
+
+Copyright (C) 2002-2026 Sebastiano Vigna
+
+Source: https://github.com/vigna/fastutil
+
+### License
+```
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+```
@@ -36,16 +36,16 @@ import jdk.test.lib.process.ProcessTools;
public class PrimitiveHashSetTest {
private static final String PRIMITIVE_HASH_SET_FIELD = "primitiveHashSet";
private static final String[] PRIMITIVE_HASH_SET_CLASSES = {
- "java.util.HashSet$PrimitiveHashSet",
- "java.util.HashSet$PrimitiveHashSet$1",
- "java.util.HashSet$LongHashSet",
- "java.util.HashSet$LongHashSet$LongIterator",
- "java.util.HashSet$LongHashSet$LongSpliterator",
- "java.util.HashSet$LongHashSet$LongArrayList",
- "java.util.HashSet$IntHashSet",
- "java.util.HashSet$IntHashSet$IntIterator",
- "java.util.HashSet$IntHashSet$IntSpliterator",
- "java.util.HashSet$IntHashSet$IntArrayList"
+ "java.util.PrimitiveHashSet",
+ "java.util.PrimitiveHashSet$1",
+ "java.util.LongHashSet",
+ "java.util.LongHashSet$LongIterator",
+ "java.util.LongHashSet$LongSpliterator",
+ "java.util.LongHashSet$LongArrayList",
+ "java.util.IntHashSet",
+ "java.util.IntHashSet$IntIterator",
+ "java.util.IntHashSet$IntSpliterator",
+ "java.util.IntHashSet$IntArrayList"
};
public static void main(String[] args) throws Exception {
@@ -87,9 +87,10 @@ public class PrimitiveHashSetTest {
}
if (expected) {
int modifiers = primitiveHashSetClass.getModifiers();
- if (!Modifier.isPrivate(modifiers) || !Modifier.isStatic(modifiers)
- || !Modifier.isAbstract(modifiers)) {
- throw new RuntimeException("HashSet.PrimitiveHashSet must be private static abstract");
+ if (!Modifier.isAbstract(modifiers) || Modifier.isPublic(modifiers)
+ || Modifier.isProtected(modifiers) || Modifier.isPrivate(modifiers)
+ || Modifier.isStatic(modifiers)) {
+ throw new RuntimeException("PrimitiveHashSet must be package-private abstract");
}
if (field.getType() != primitiveHashSetClass || !Modifier.isTransient(field.getModifiers())
|| Modifier.isPublic(field.getModifiers())) {
@@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -71,6 +72,7 @@ public class PrimitiveHashSetBehaviorTest {
specializedIteratorRemoval();
randomizedDifferential();
streamOperations();
+ failedSpecializationFallsBack();
}
private static void constructors() {
@@ -158,6 +160,8 @@ public class PrimitiveHashSetBehaviorTest {
Iterator<Long> failFast = set.iterator();
set.add(10_000L);
+ assertTrue(failFast.hasNext());
+ assertThrows(IllegalStateException.class, failFast::remove);
assertThrows(ConcurrentModificationException.class, failFast::next);
HashSet<Long> empty = new HashSet<>();
@@ -532,7 +536,8 @@ public class PrimitiveHashSetBehaviorTest {
Iterator<Number> beforeRollback = set.iterator();
set.add(3.14d);
- assertThrows(ConcurrentModificationException.class, beforeRollback::hasNext);
+ assertTrue(beforeRollback.hasNext());
+ assertThrows(ConcurrentModificationException.class, beforeRollback::next);
HashSet<Number> values = new HashSet<>();
values.add(null);
@@ -583,6 +588,60 @@ public class PrimitiveHashSetBehaviorTest {
assertEquals(set, set.parallelStream().collect(Collectors.toSet()));
}
+ private static void failedSpecializationFallsBack() throws Exception {
+ failedSpecializationFallsBack(true);
+ failedSpecializationFallsBack(false);
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static void failedSpecializationFallsBack(boolean useLongs) throws Exception {
+ Field mapField = HashSet.class.getDeclaredField("map");
+ Field primitiveSetField = HashSet.class.getDeclaredField("primitiveHashSet");
+ Field candidateStateField = HashSet.class.getDeclaredField("primitiveCandidateState");
+ mapField.setAccessible(true);
+ primitiveSetField.setAccessible(true);
+ candidateStateField.setAccessible(true);
+
+ HashSet<Number> set = new HashSet<>();
+ FailingIterationHashMap<Number, Object> map = new FailingIterationHashMap<>();
+ for (int i = 0; i < 100; i++) {
+ map.put(useLongs ? Long.valueOf(i) : Integer.valueOf(i), new Object());
+ }
+ mapField.set(set, map);
+ candidateStateField.setInt(set, useLongs ? 1 : 2);
+
+ Number added = useLongs ? Long.valueOf(100) : Integer.valueOf(100);
+ assertTrue(set.add(added));
+ assertSame(map, mapField.get(set));
+ assertNull(primitiveSetField.get(set));
+ assertEquals(-1, candidateStateField.getInt(set));
+ assertTrue(set.contains(added));
+
+ Number next = useLongs ? Long.valueOf(101) : Integer.valueOf(101);
+ assertTrue(set.add(next));
+ assertSame(map, mapField.get(set));
+ assertNull(primitiveSetField.get(set));
+ assertTrue(set.contains(next));
+ }
+
+ private static final class FailingIterationHashMap<K, V> extends HashMap<K, V> {
+ @Override
+ public Set<K> keySet() {
+ Set<K> keys = super.keySet();
+ return new java.util.AbstractSet<>() {
+ @Override
+ public Iterator<K> iterator() {
+ throw new ConcurrentModificationException();
+ }
+
+ @Override
+ public int size() {
+ return keys.size();
+ }
+ };
+ }
+ }
+
private static void randomizedDifferential() {
runRandomizedDifferential(true);
runRandomizedDifferential(false);
@@ -256,6 +256,7 @@ public class HashSet<E>
*/
public boolean add(E e) {
PrimitiveHashSet specializedSet = primitiveHashSet;
+ HashMap<E, Object> currentMap = map;
if (specializedSet != null) {
if (e == null) {
return specializedSet.addNull();
@@ -267,15 +268,16 @@ public class HashSet<E>
} else if (e instanceof Integer key) {
return ((IntHashSet) specializedSet).addInt(key.intValue());
}
- rollbackToHashMap();
- return this.map.put(e, PRESENT) == null;
+ HashMap<E, Object> rollbackMap = rollbackToHashMap(specializedSet);
+ return rollbackMap.put(e, PRESENT) == null;
+ } else if (currentMap != null) {
+ return addToMap(e, currentMap);
}
-
- return addToMap(e);
+ return false;
}
- private boolean addToMap(E e) {
- boolean result = map.put(e, PRESENT) == null;
+ private boolean addToMap(E e, HashMap<E, Object> currentMap) {
+ boolean result = currentMap.put(e, PRESENT) == null;
if (!result) {
return false;
}
@@ -284,7 +286,7 @@ public class HashSet<E>
}
// LinkedHashSet must retain LinkedHashMap ordering and cannot switch
// to the unordered primitive HashSet representation.
- if (map instanceof LinkedHashMap || getClass() != HashSet.class) {
+ if (currentMap instanceof LinkedHashMap || getClass() != HashSet.class) {
primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
return true;
}
@@ -297,15 +299,15 @@ public class HashSet<E>
&& (primitiveCandidateState == NO_PRIMITIVE_CANDIDATE
|| primitiveCandidateState == LONG_PRIMITIVE_CANDIDATE)) {
primitiveCandidateState = LONG_PRIMITIVE_CANDIDATE;
- if (map.size() >= PRIMITIVE_SPECIALIZATION_THRESHOLD) {
- switchToLongHashSet();
+ if (currentMap.size() >= PRIMITIVE_SPECIALIZATION_THRESHOLD) {
+ switchToLongHashSet(currentMap);
}
} else if (e instanceof Integer
&& (primitiveCandidateState == NO_PRIMITIVE_CANDIDATE
|| primitiveCandidateState == INT_PRIMITIVE_CANDIDATE)) {
primitiveCandidateState = INT_PRIMITIVE_CANDIDATE;
- if (map.size() >= PRIMITIVE_SPECIALIZATION_THRESHOLD) {
- switchToIntHashSet();
+ if (currentMap.size() >= PRIMITIVE_SPECIALIZATION_THRESHOLD) {
+ switchToIntHashSet(currentMap);
}
} else {
primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
@@ -577,1273 +579,92 @@ public class HashSet<E>
return super.hashCode();
}
- private void switchToLongHashSet() {
+ private void switchToLongHashSet(HashMap<E, Object> sourceMap) {
if (primitiveHashSet != null) {
return;
}
- LongHashSet newLongHashSet = new LongHashSet();
- newLongHashSet.hashMapLoadFactor = this.map.loadFactor();
- for (E key : this.map.keySet()) {
- if (key == null) {
- newLongHashSet.addNull();
- } else if (key.getClass() == Long.class) {
- newLongHashSet.addLong(((Long) key).longValue());
- } else {
- this.primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
+ try {
+ int expectedModCount = sourceMap.modCount;
+ LongHashSet newLongHashSet = new LongHashSet();
+ newLongHashSet.hashMapLoadFactor = sourceMap.loadFactor();
+ for (E key : sourceMap.keySet()) {
+ if (key == null) {
+ newLongHashSet.addNull();
+ } else if (key.getClass() == Long.class) {
+ newLongHashSet.addLong(((Long) key).longValue());
+ } else {
+ primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
+ return;
+ }
+ }
+ if (sourceMap.modCount != expectedModCount || map != sourceMap
+ || primitiveHashSet != null) {
+ disablePrimitiveSpecialization(sourceMap);
return;
}
+ // Publish only after the temporary set has been fully populated.
+ primitiveHashSet = newLongHashSet;
+ primitiveCandidateState = NO_PRIMITIVE_CANDIDATE;
+ map = null;
+ } catch (RuntimeException e) {
+ disablePrimitiveSpecialization(sourceMap);
}
- this.primitiveHashSet = newLongHashSet;
- this.primitiveCandidateState = 0;
- this.map = null;
}
- private void switchToIntHashSet() {
+ private void switchToIntHashSet(HashMap<E, Object> sourceMap) {
if (primitiveHashSet != null) {
return;
}
- IntHashSet newIntHashSet = new IntHashSet();
- newIntHashSet.hashMapLoadFactor = this.map.loadFactor();
- for (E key : this.map.keySet()) {
- if (key == null) {
- newIntHashSet.addNull();
- } else if (key.getClass() == Integer.class) {
- newIntHashSet.addInt(((Integer) key).intValue());
- } else {
- this.primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
+ try {
+ int expectedModCount = sourceMap.modCount;
+ IntHashSet newIntHashSet = new IntHashSet();
+ newIntHashSet.hashMapLoadFactor = sourceMap.loadFactor();
+ for (E key : sourceMap.keySet()) {
+ if (key == null) {
+ newIntHashSet.addNull();
+ } else if (key.getClass() == Integer.class) {
+ newIntHashSet.addInt(((Integer) key).intValue());
+ } else {
+ primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
+ return;
+ }
+ }
+ if (sourceMap.modCount != expectedModCount || map != sourceMap
+ || primitiveHashSet != null) {
+ disablePrimitiveSpecialization(sourceMap);
return;
}
+ // Publish only after the temporary set has been fully populated.
+ primitiveHashSet = newIntHashSet;
+ primitiveCandidateState = NO_PRIMITIVE_CANDIDATE;
+ map = null;
+ } catch (RuntimeException e) {
+ disablePrimitiveSpecialization(sourceMap);
}
- this.primitiveHashSet = newIntHashSet;
- this.primitiveCandidateState = 0;
- this.map = null;
}
- private void rollbackToHashMap() {
- if (this.primitiveHashSet == null) {
- return;
+ private void disablePrimitiveSpecialization(HashMap<E, Object> sourceMap) {
+ if (map == sourceMap && primitiveHashSet == null) {
+ primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
}
- float loadFactor = this.primitiveHashSet.hashMapLoadFactor;
+ }
+
+ private HashMap<E, Object> rollbackToHashMap(PrimitiveHashSet specializedSet) {
+ float loadFactor = specializedSet.hashMapLoadFactor;
int capacity = (int) Math.min(
- Math.ceil(size() / (double) loadFactor),
+ Math.ceil(specializedSet.size() / (double) loadFactor),
HashMap.MAXIMUM_CAPACITY);
HashMap<E, Object> newMap = new HashMap<>(capacity, loadFactor);
- for (Number key : this.primitiveHashSet) {
+ for (Number key : specializedSet) {
@SuppressWarnings("unchecked")
E element = (E) key;
newMap.put(element, PRESENT);
}
- this.primitiveHashSet.invalidateIterators();
+ specializedSet.invalidateIterators();
this.map = newMap;
this.primitiveHashSet = null;
this.primitiveCandidateState = PRIMITIVE_SPECIALIZATION_DISABLED;
+ return newMap;
}
- private static abstract class PrimitiveHashSet extends AbstractSet<Number> implements Cloneable {
- int mask;
-
- boolean containsNull;
-
- boolean containsZero;
-
- int capacity;
-
- int threshold;
-
- int minCapacity;
-
- int size;
-
- int modCount;
-
- float loadFactor;
-
- // Retained only to emit the standard HashSet serialization metadata.
- // It is intentionally independent of this table's occupancy factor.
- float hashMapLoadFactor = HashMap.DEFAULT_LOAD_FACTOR;
-
- static final long LONG_PHI = 0x9E3779B97F4A7C15L;
-
- static final int DEFAULT_INITIAL_CAPACITY = 16;
-
- static final float DEFAULT_LOAD_FACTOR = .75f;
-
- public PrimitiveHashSet(final int initialCapacity, final float loadFactor) {
- if (loadFactor <= 0 || Float.isNaN(loadFactor) || loadFactor >= 1) {
- throw new IllegalArgumentException("Load factor must be between 0 and 1");
- }
- if (initialCapacity < 0) {
- throw new IllegalArgumentException("Initial capacity must be non-negative");
- }
- this.loadFactor = loadFactor;
- this.minCapacity = this.capacity = PrimitiveHashSet.leastArraySize(initialCapacity, loadFactor);
- this.mask = this.capacity - 1;
- this.threshold = PrimitiveHashSet.maxEntriesFill(this.capacity, loadFactor);
- }
-
- protected int realSize() {
- return this.size - (this.containsZero ? 1 : 0) - (this.containsNull ? 1 : 0);
- }
-
- protected static long mixBits(final long n) {
- long res = n * LONG_PHI;
- res ^= res >>> 32;
- return res ^ (res >>> 16);
- }
-
- protected static int maxEntriesFill(int capacity, float loadFactor) {
- return Math.min((int) Math.ceil(capacity * loadFactor), capacity - 1);
- }
-
- protected static int leastArraySize(int expectedElements, float loadFactor) {
- final long size = Math.max(2, nextPowerOfTwo((long) Math.ceil(expectedElements / loadFactor)));
- if (size > (1 << 30)) {
- throw new IllegalArgumentException("Expected number of elements is too large: " + expectedElements
- + " with load factor " + loadFactor);
- }
- return (int) size;
- }
-
- protected static long nextPowerOfTwo(long n) {
- if (n == 0) {
- return 1;
- }
- n--;
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- return (n | n >> 32) + 1;
- }
-
- @Override
- public int size() {
- return this.size;
- }
-
- public boolean isEmpty() {
- return this.size == 0;
- }
-
-
- abstract Iterator<? extends Number> valueIterator();
-
- abstract void forEachElement(Consumer<? super Number> action);
-
- final boolean addNull() {
- if (containsNull) {
- return false;
- }
- containsNull = true;
- size++;
- modCount++;
- return true;
- }
-
- final boolean removeNull() {
- if (!containsNull) {
- return false;
- }
- containsNull = false;
- size--;
- modCount++;
- return true;
- }
-
- final void invalidateIterators() {
- modCount++;
- }
-
- @Override
- public final Iterator<Number> iterator() {
- return new Iterator<>() {
- private final Iterator<? extends Number> values = valueIterator();
- private int expectedModCount = modCount;
- private boolean nullPending = containsNull;
- private boolean canRemove;
- private boolean lastWasNull;
-
- private void checkForComodification() {
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
-
- @Override
- public boolean hasNext() {
- checkForComodification();
- return nullPending || values.hasNext();
- }
-
- @Override
- public Number next() {
- checkForComodification();
- if (nullPending) {
- nullPending = false;
- canRemove = true;
- lastWasNull = true;
- return null;
- }
- Number value = values.next();
- canRemove = true;
- lastWasNull = false;
- return value;
- }
-
- @Override
- public void remove() {
- checkForComodification();
- if (!canRemove) {
- throw new IllegalStateException();
- }
- if (lastWasNull) {
- removeNull();
- } else {
- values.remove();
- }
- expectedModCount = modCount;
- canRemove = false;
- }
- };
- }
-
- @Override
- public final void forEach(Consumer<? super Number> action) {
- Objects.requireNonNull(action);
- int expectedModCount = modCount;
- if (containsNull) {
- action.accept(null);
- }
- forEachElement(action);
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
-
- @Override
- public abstract boolean contains(Object e);
-
- @Override
- public abstract boolean remove(Object e);
-
- public PrimitiveHashSet clone() {
- try {
- PrimitiveHashSet cloned = (PrimitiveHashSet) super.clone();
- return cloned;
- } catch (CloneNotSupportedException e) {
- throw new InternalError(e);
- }
- }
- }
-
- private static class LongHashSet extends PrimitiveHashSet {
- long[] keys;
- public LongHashSet() {
- this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
- }
-
- public LongHashSet(final int initialCapacity, final float loadFactor) {
- super(initialCapacity, loadFactor);
- this.keys = new long[this.capacity + 1];
- }
-
- boolean addLong(long key) {
- int pos;
- if (key == 0L) {
- if (this.containsZero) {
- return false;
- }
- this.containsZero = true;
- } else {
- long current;
- final long[] curKeys = this.keys;
- if (!((current = curKeys[pos = (int) LongHashSet.mixBits(key) & this.mask]) == 0)) {
- if (current == key) {
- return false;
- }
- while (!((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
- if (current == key) {
- return false;
- }
- }
- }
- curKeys[pos] = key;
- }
- if (this.size++ >= this.threshold) {
- rehash(LongHashSet.leastArraySize(this.size + 1, this.loadFactor));
- }
- this.modCount++;
- return true;
- }
-
- @Override
- public boolean remove(Object key) {
- if (key == null) {
- return removeNull();
- }
- return key instanceof Long && removeLong((Long) key);
- }
-
- boolean removeLong(long key) {
- if (key == 0L) {
- if (this.containsZero) {
- return removeNullEntry();
- }
- return false;
- }
- long current;
- final long[] curKeys = this.keys;
- int pos;
- if (((current = curKeys[pos = (int) LongHashSet.mixBits(key) & this.mask]) == 0)) {
- return false;
- }
- if (current == key) {
- return removeEntry(pos);
- }
- while (true) {
- if (((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
- return false;
- }
- if (current == key) {
- return removeEntry(pos);
- }
- }
- }
-
- @Override
- public boolean contains(Object key) {
- if (key == null) {
- return this.containsNull;
- }
- return key instanceof Long && containsLong((Long) key);
- }
-
- boolean containsLong(long key) {
- if (key == 0L) return this.containsZero;
- long current;
- final long[] curKeys = this.keys;
- int pos = (int) LongHashSet.mixBits(key) & this.mask;
- if ((current = curKeys[pos]) == 0) return false;
- if (current == key) return true;
- while ((current = curKeys[pos = (pos + 1) & this.mask]) != 0) {
- if (current == key) return true;
- }
- return false;
- }
-
-
- Iterator<Long> valueIterator() {
- return new LongIterator();
- }
-
- @Override
- public Spliterator<Number> spliterator() {
- return new LongSpliterator(0, -1, 0, 0, false, false, false);
- }
-
- @Override
- void forEachElement(Consumer<? super Number> action) {
- if (containsZero) {
- action.accept(Long.valueOf(0));
- }
- final long[] curKeys = keys;
- for (int i = capacity; i-- != 0;) {
- long key = curKeys[i];
- if (key != 0) {
- action.accept(Long.valueOf(key));
- }
- }
- }
-
- public void clear() {
- if (this.size == 0) {
- return;
- }
- this.size = 0;
- this.containsZero = false;
- this.containsNull = false;
- Arrays.fill(this.keys, 0);
- this.modCount++;
- }
-
- @Override
- public LongHashSet clone() {
- LongHashSet newSet = (LongHashSet) super.clone();
- newSet.keys = this.keys.clone();
- return newSet;
- }
-
- @Override
- public int hashCode() {
- int h = 0;
- for (int j = realSize(), i = 0; j-- != 0;) {
- while (this.keys[i] == 0) {
- i++;
- }
- h += (int) (this.keys[i] ^ (this.keys[i] >>> 32));
- i++;
- }
- return h;
- }
-
- private boolean removeEntry(int pos) {
- this.size--;
- shiftKeys(pos);
- if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
- && this.capacity > DEFAULT_INITIAL_CAPACITY) {
- rehash(this.capacity >> 1);
- }
- this.modCount++;
- return true;
- }
-
- private void shiftKeys(int pos) {
- int last, slot;
- long current;
- final long[] curKeys = this.keys;
- for (;;) {
- pos = ((last = pos) + 1) & this.mask;
- for (;;) {
- if (((current = curKeys[pos]) == 0)) {
- curKeys[last] = 0;
- return;
- }
- slot = (int) LongHashSet.mixBits(current) & this.mask;
- if (last <= pos ? (last >= slot || slot > pos) : (last >= slot && slot > pos)) {
- break;
- }
- pos = (pos + 1) & this.mask;
- }
- curKeys[last] = current;
- }
- }
-
- private boolean removeNullEntry() {
- this.containsZero = false;
- this.keys[this.capacity] = 0;
- this.size--;
- if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
- && this.capacity > DEFAULT_INITIAL_CAPACITY) {
- rehash(this.capacity >> 1);
- }
- this.modCount++;
- return true;
- }
-
- private void rehash(final int newCapacity) {
- final long[] oldKeys = this.keys;
- final int newMask = newCapacity - 1;
- final long[] newKeys = new long[newCapacity + 1];
- int i = this.capacity, pos;
- for (int j = realSize(); j-- != 0;) {
- while ((oldKeys[--i] == 0))
- ;
- if (!((newKeys[pos = (int) LongHashSet.mixBits(oldKeys[i]) & newMask]) == 0)) {
- while (!((newKeys[pos = (pos + 1) & newMask]) == 0))
- ;
- }
- newKeys[pos] = oldKeys[i];
- }
- this.capacity = newCapacity;
- this.mask = newMask;
- this.threshold = LongHashSet.maxEntriesFill(this.capacity, this.loadFactor);
- this.keys = newKeys;
- }
-
- private final class LongIterator implements PrimitiveIterator.OfLong {
- private int position = LongHashSet.this.capacity;
- private int last = -1;
- private int counter = LongHashSet.this.size - (LongHashSet.this.containsNull ? 1 : 0);
- private boolean mustReturnZero = LongHashSet.this.containsZero;
- private LongArrayList wrapped;
-
- @Override
- public long nextLong() {
- if (!hasNext()) {
- throw new java.util.NoSuchElementException();
- }
- this.counter--;
- if (this.mustReturnZero) {
- this.mustReturnZero = false;
- last = LongHashSet.this.capacity;
- return LongHashSet.this.keys[LongHashSet.this.capacity];
- }
- final long[] curKeys = LongHashSet.this.keys;
- for (;;) {
- if (--this.position < 0) {
- this.last = Integer.MIN_VALUE;
- return this.wrapped.getLong(-this.position - 1);
- }
- if (!(curKeys[this.position] == 0)) {
- return curKeys[this.last = this.position];
- }
- }
- }
-
- @Override
- public boolean hasNext() {
- return this.counter != 0;
- }
-
- private final void shiftKeys(int pos) {
- int lastPos, slot;
- long current;
- final long[] curKeys = LongHashSet.this.keys;
- for (;;) {
- pos = ((lastPos = pos) + 1) & LongHashSet.this.mask;
- for (;;) {
- if (((current = curKeys[pos]) == 0)) {
- curKeys[lastPos] = 0;
- return;
- }
- slot = (int) LongHashSet.mixBits(current) & LongHashSet.this.mask;
- if (lastPos <= pos ? (lastPos >= slot || slot > pos) : (lastPos >= slot && slot > pos)) {
- break;
- }
- pos = (pos + 1) & LongHashSet.this.mask;
- }
- if (pos < lastPos) {
- if (wrapped == null) {
- wrapped = new LongArrayList(2);
- }
- wrapped.add(curKeys[pos]);
- }
- curKeys[lastPos] = current;
- }
- }
-
- @Override
- public void remove() {
- if (this.last == -1) {
- throw new IllegalStateException();
- }
- if (this.last == LongHashSet.this.capacity) {
- LongHashSet.this.containsZero = false;
- LongHashSet.this.keys[LongHashSet.this.capacity] = 0;
- } else if (this.position >= 0) {
- shiftKeys(this.last);
- } else {
- LongHashSet.this.remove(this.wrapped.getLong(-this.position - 1));
- this.last = -1;
- return;
- }
- LongHashSet.this.size--;
- LongHashSet.this.modCount++;
- this.last = -1;
- }
-
- }
-
- private final class LongSpliterator implements Spliterator<Number> {
- private int position;
- private int fence;
- private int expectedModCount;
- private long estimatedSize;
- private boolean nullPending;
- private boolean zeroPending;
- private boolean hasSplit;
-
- LongSpliterator(int position, int fence, int expectedModCount,
- long estimatedSize, boolean nullPending,
- boolean zeroPending, boolean hasSplit) {
- this.position = position;
- this.fence = fence;
- this.expectedModCount = expectedModCount;
- this.estimatedSize = estimatedSize;
- this.nullPending = nullPending;
- this.zeroPending = zeroPending;
- this.hasSplit = hasSplit;
- }
-
- private int getFence() {
- if (fence < 0) {
- expectedModCount = modCount;
- estimatedSize = size;
- nullPending = containsNull;
- zeroPending = containsZero;
- fence = capacity;
- }
- return fence;
- }
-
- private void checkForComodification() {
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
-
- @Override
- public Spliterator<Number> trySplit() {
- int high = getFence();
- int low = position;
- int middle = (low + high) >>> 1;
- if (low >= middle) {
- return null;
- }
- long splitEstimate = estimatedSize >>> 1;
- LongSpliterator split = new LongSpliterator(low, middle,
- expectedModCount, splitEstimate, nullPending,
- zeroPending, true);
- position = middle;
- estimatedSize -= splitEstimate;
- nullPending = false;
- zeroPending = false;
- hasSplit = true;
- return split;
- }
-
- @Override
- public boolean tryAdvance(Consumer<? super Number> action) {
- Objects.requireNonNull(action);
- int high = getFence();
- if (nullPending) {
- nullPending = false;
- if (estimatedSize > 0) {
- estimatedSize--;
- }
- action.accept(null);
- checkForComodification();
- return true;
- }
- if (zeroPending) {
- zeroPending = false;
- if (estimatedSize > 0) {
- estimatedSize--;
- }
- action.accept(Long.valueOf(0L));
- checkForComodification();
- return true;
- }
- final long[] curKeys = keys;
- while (position < high) {
- long key = curKeys[position++];
- if (key != 0L) {
- if (estimatedSize > 0) {
- estimatedSize--;
- }
- action.accept(Long.valueOf(key));
- checkForComodification();
- return true;
- }
- }
- checkForComodification();
- return false;
- }
-
- @Override
- public void forEachRemaining(Consumer<? super Number> action) {
- Objects.requireNonNull(action);
- int high = getFence();
- if (nullPending) {
- nullPending = false;
- action.accept(null);
- }
- if (zeroPending) {
- zeroPending = false;
- action.accept(Long.valueOf(0L));
- }
- final long[] curKeys = keys;
- while (position < high) {
- long key = curKeys[position++];
- if (key != 0L) {
- action.accept(Long.valueOf(key));
- }
- }
- estimatedSize = 0;
- checkForComodification();
- }
-
- @Override
- public long estimateSize() {
- getFence();
- return estimatedSize;
- }
-
- @Override
- public int characteristics() {
- return Spliterator.DISTINCT | (hasSplit ? 0 : Spliterator.SIZED);
- }
- }
-
- private static class LongArrayList {
- /** The initial default capacity of an array list. */
- public static final int DEFAULT_INITIAL_CAPACITY = 10;
- /** The backing array. */
- protected long a[];
- /**
- * The current actual size of the list (never greater than the backing-array
- * length).
- */
- protected int size;
-
- private static final long[] DEFAULT_EMPTY_ARRAY = {};
-
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
- * Creates a new array list with given capacity.
- *
- * @param capacity the initial capacity of the array list (may be 0).
- */
- public LongArrayList(final int capacity) {
- initArrayFromCapacity(capacity);
- }
-
- private void initArrayFromCapacity(final int capacity) {
- if (capacity < 0)
- throw new IllegalArgumentException("Initial capacity (" + capacity + ") is negative");
- if (capacity == 0)
- a = LongArrayList.DEFAULT_EMPTY_ARRAY;
- else
- a = new long[capacity];
- }
-
- public static long[] forceCapacity(final long[] array, final int length, final int preserve) {
- final long t[] = new long[length];
- System.arraycopy(array, 0, t, 0, preserve);
- return t;
- }
-
- /**
- * Grows this array list, ensuring that it can contain the given number of
- * entries without resizing,
- * and in case increasing the current capacity at least by a factor of 50%.
- *
- * @param capacity the new minimum capacity for this array list.
- */
-
- private void grow(int capacity) {
- if (capacity <= a.length)
- return;
- if (a != LongArrayList.DEFAULT_EMPTY_ARRAY)
- capacity = (int) Math.max(Math.min((long) a.length + (a.length >> 1), LongArrayList.MAX_ARRAY_SIZE),
- capacity);
- else if (capacity < DEFAULT_INITIAL_CAPACITY)
- capacity = DEFAULT_INITIAL_CAPACITY;
- a = LongArrayList.forceCapacity(a, capacity, size);
- assert size <= a.length;
- }
-
- public boolean add(final long k) {
- grow(size + 1);
- a[size++] = k;
- assert size <= a.length;
- return true;
- }
-
- public long getLong(final int index) {
- if (index >= size)
- throw new IndexOutOfBoundsException(
- "Index (" + index + ") is greater than or equal to list size (" + size + ")");
- return a[index];
- }
-
- }
- }
-
- private static class IntHashSet extends PrimitiveHashSet {
- int[] keys;
-
- public IntHashSet() {
- this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
- }
-
- public IntHashSet(final int initialCapacity, final float loadFactor) {
- super(initialCapacity, loadFactor);
- this.keys = new int[this.capacity + 1];
- }
-
- boolean addInt(int key) {
- int pos;
- if (key == 0) {
- if (this.containsZero) {
- return false;
- }
- this.containsZero = true;
- } else {
- int current;
- final int[] curKeys = this.keys;
- if (!((current = curKeys[pos = (int) IntHashSet.mixBits(key) & this.mask]) == 0)) {
- if (current == key) {
- return false;
- }
- while (!((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
- if (current == key) {
- return false;
- }
- }
- }
- curKeys[pos] = key;
- }
- if (this.size++ >= this.threshold) {
- rehash(IntHashSet.leastArraySize(this.size + 1, this.loadFactor));
- }
- this.modCount++;
- return true;
- }
-
- @Override
- public boolean remove(Object key) {
- if (key == null) {
- return removeNull();
- }
- return key instanceof Integer && removeInt((Integer) key);
- }
-
- boolean removeInt(int key) {
- if (key == 0) {
- if (this.containsZero) {
- return removeNullEntry();
- }
- return false;
- }
- int current;
- final int[] curKeys = this.keys;
- int pos;
- if (((current = curKeys[pos = (int) IntHashSet.mixBits(key) & this.mask]) == 0)) {
- return false;
- }
- if (current == key) {
- return removeEntry(pos);
- }
- while (true) {
- if (((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
- return false;
- }
- if (current == key) {
- return removeEntry(pos);
- }
- }
- }
-
- @Override
- public boolean contains(Object key) {
- if (key == null) {
- return this.containsNull;
- }
- return key instanceof Integer && containsInt((Integer) key);
- }
-
- boolean containsInt(int key) {
- if (key == 0) return this.containsZero;
- int current;
- final int[] curKeys = this.keys;
- int pos = (int) IntHashSet.mixBits(key) & this.mask;
- if ((current = curKeys[pos]) == 0) return false;
- if (current == key) return true;
- while ((current = curKeys[pos = (pos + 1) & this.mask]) != 0) {
- if (current == key) return true;
- }
- return false;
- }
-
- Iterator<Integer> valueIterator() {
- return new IntIterator();
- }
-
- @Override
- public Spliterator<Number> spliterator() {
- return new IntSpliterator(0, -1, 0, 0, false, false, false);
- }
-
- @Override
- void forEachElement(Consumer<? super Number> action) {
- if (containsZero) {
- action.accept(Integer.valueOf(0));
- }
- final int[] curKeys = keys;
- for (int i = capacity; i-- != 0;) {
- int key = curKeys[i];
- if (key != 0) {
- action.accept(Integer.valueOf(key));
- }
- }
- }
-
- public void clear() {
- if (this.size == 0) {
- return;
- }
- this.size = 0;
- this.containsZero = false;
- this.containsNull = false;
- Arrays.fill(this.keys, 0);
- this.modCount++;
- }
-
- @Override
- public IntHashSet clone() {
- IntHashSet newSet = (IntHashSet) super.clone();
- newSet.keys = this.keys.clone();
- return newSet;
- }
-
- @Override
- public int hashCode() {
- int h = 0;
- for (int j = realSize(), i = 0; j-- != 0;) {
- while (this.keys[i] == 0) {
- i++;
- }
- h += this.keys[i];
- i++;
- }
- return h;
- }
-
- private boolean removeEntry(int pos) {
- this.size--;
- shiftKeys(pos);
- if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
- && this.capacity > DEFAULT_INITIAL_CAPACITY) {
- rehash(this.capacity >> 1);
- }
- this.modCount++;
- return true;
- }
-
- private void shiftKeys(int pos) {
- int last, slot;
- int current;
- final int[] curKeys = this.keys;
- for (;;) {
- pos = ((last = pos) + 1) & this.mask;
- for (;;) {
- if (((current = curKeys[pos]) == 0)) {
- curKeys[last] = 0;
- return;
- }
- slot = (int) IntHashSet.mixBits(current) & this.mask;
- if (last <= pos ? (last >= slot || slot > pos) : (last >= slot && slot > pos)) {
- break;
- }
- pos = (pos + 1) & this.mask;
- }
- curKeys[last] = current;
- }
- }
-
- private boolean removeNullEntry() {
- this.containsZero = false;
- this.keys[this.capacity] = 0;
- this.size--;
- if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
- && this.capacity > DEFAULT_INITIAL_CAPACITY) {
- rehash(this.capacity >> 1);
- }
- this.modCount++;
- return true;
- }
-
- private void rehash(final int newCapacity) {
- final int[] oldKeys = this.keys;
- final int newMask = newCapacity - 1;
- final int[] newKeys = new int[newCapacity + 1];
- int i = this.capacity, pos;
- for (int j = realSize(); j-- != 0;) {
- while ((oldKeys[--i] == 0))
- ;
- if (!((newKeys[pos = (int) IntHashSet.mixBits(oldKeys[i]) & newMask]) == 0)) {
- while (!((newKeys[pos = (pos + 1) & newMask]) == 0))
- ;
- }
- newKeys[pos] = oldKeys[i];
- }
- this.capacity = newCapacity;
- this.mask = newMask;
- this.threshold = IntHashSet.maxEntriesFill(this.capacity, this.loadFactor);
- this.keys = newKeys;
- }
-
- private final class IntIterator implements PrimitiveIterator.OfInt {
- private int position = IntHashSet.this.capacity;
- private int last = -1;
- private int counter = IntHashSet.this.size - (IntHashSet.this.containsNull ? 1 : 0);
- private boolean mustReturnZero = IntHashSet.this.containsZero;
- private IntArrayList wrapped;
-
- @Override
- public int nextInt() {
- if (!hasNext()) {
- throw new java.util.NoSuchElementException();
- }
- this.counter--;
- if (this.mustReturnZero) {
- this.mustReturnZero = false;
- last = IntHashSet.this.capacity;
- return IntHashSet.this.keys[IntHashSet.this.capacity];
- }
- final int[] curKeys = IntHashSet.this.keys;
- for (;;) {
- if (--this.position < 0) {
- this.last = Integer.MIN_VALUE;
- return this.wrapped.getInt(-this.position - 1);
- }
- if (!(curKeys[this.position] == 0)) {
- return curKeys[this.last = this.position];
- }
- }
- }
-
- @Override
- public boolean hasNext() {
- return this.counter != 0;
- }
-
- private final void shiftKeys(int pos) {
- int lastPos, slot;
- int current;
- final int[] curKeys = IntHashSet.this.keys;
- for (;;) {
- pos = ((lastPos = pos) + 1) & IntHashSet.this.mask;
- for (;;) {
- if (((current = curKeys[pos]) == 0)) {
- curKeys[lastPos] = 0;
- return;
- }
- slot = (int) IntHashSet.mixBits(current) & IntHashSet.this.mask;
- if (lastPos <= pos ? (lastPos >= slot || slot > pos) : (lastPos >= slot && slot > pos)) {
- break;
- }
- pos = (pos + 1) & IntHashSet.this.mask;
- }
- if (pos < lastPos) {
- if (wrapped == null) {
- wrapped = new IntArrayList(2);
- }
- wrapped.add(curKeys[pos]);
- }
- curKeys[lastPos] = current;
- }
- }
-
- @Override
- public void remove() {
- if (this.last == -1) {
- throw new IllegalStateException();
- }
- if (this.last == IntHashSet.this.capacity) {
- IntHashSet.this.containsZero = false;
- IntHashSet.this.keys[IntHashSet.this.capacity] = 0;
- } else if (this.position >= 0) {
- shiftKeys(this.last);
- } else {
- IntHashSet.this.remove(this.wrapped.getInt(-this.position - 1));
- this.last = -1;
- return;
- }
- IntHashSet.this.size--;
- IntHashSet.this.modCount++;
- this.last = -1;
- }
-
- }
-
- private final class IntSpliterator implements Spliterator<Number> {
- private int position;
- private int fence;
- private int expectedModCount;
- private long estimatedSize;
- private boolean nullPending;
- private boolean zeroPending;
- private boolean hasSplit;
-
- IntSpliterator(int position, int fence, int expectedModCount,
- long estimatedSize, boolean nullPending,
- boolean zeroPending, boolean hasSplit) {
- this.position = position;
- this.fence = fence;
- this.expectedModCount = expectedModCount;
- this.estimatedSize = estimatedSize;
- this.nullPending = nullPending;
- this.zeroPending = zeroPending;
- this.hasSplit = hasSplit;
- }
-
- private int getFence() {
- if (fence < 0) {
- expectedModCount = modCount;
- estimatedSize = size;
- nullPending = containsNull;
- zeroPending = containsZero;
- fence = capacity;
- }
- return fence;
- }
-
- private void checkForComodification() {
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
-
- @Override
- public Spliterator<Number> trySplit() {
- int high = getFence();
- int low = position;
- int middle = (low + high) >>> 1;
- if (low >= middle) {
- return null;
- }
- long splitEstimate = estimatedSize >>> 1;
- IntSpliterator split = new IntSpliterator(low, middle,
- expectedModCount, splitEstimate, nullPending,
- zeroPending, true);
- position = middle;
- estimatedSize -= splitEstimate;
- nullPending = false;
- zeroPending = false;
- hasSplit = true;
- return split;
- }
-
- @Override
- public boolean tryAdvance(Consumer<? super Number> action) {
- Objects.requireNonNull(action);
- int high = getFence();
- if (nullPending) {
- nullPending = false;
- if (estimatedSize > 0) {
- estimatedSize--;
- }
- action.accept(null);
- checkForComodification();
- return true;
- }
- if (zeroPending) {
- zeroPending = false;
- if (estimatedSize > 0) {
- estimatedSize--;
- }
- action.accept(Integer.valueOf(0));
- checkForComodification();
- return true;
- }
- final int[] curKeys = keys;
- while (position < high) {
- int key = curKeys[position++];
- if (key != 0) {
- if (estimatedSize > 0) {
- estimatedSize--;
- }
- action.accept(Integer.valueOf(key));
- checkForComodification();
- return true;
- }
- }
- checkForComodification();
- return false;
- }
-
- @Override
- public void forEachRemaining(Consumer<? super Number> action) {
- Objects.requireNonNull(action);
- int high = getFence();
- if (nullPending) {
- nullPending = false;
- action.accept(null);
- }
- if (zeroPending) {
- zeroPending = false;
- action.accept(Integer.valueOf(0));
- }
- final int[] curKeys = keys;
- while (position < high) {
- int key = curKeys[position++];
- if (key != 0) {
- action.accept(Integer.valueOf(key));
- }
- }
- estimatedSize = 0;
- checkForComodification();
- }
-
- @Override
- public long estimateSize() {
- getFence();
- return estimatedSize;
- }
-
- @Override
- public int characteristics() {
- return Spliterator.DISTINCT | (hasSplit ? 0 : Spliterator.SIZED);
- }
- }
-
- private static class IntArrayList {
- /** The initial default capacity of an array list. */
- public static final int DEFAULT_INITIAL_CAPACITY = 10;
- /** The backing array. */
- protected int a[];
- /**
- * The current actual size of the list (never greater than the backing-array
- * length).
- */
- protected int size;
-
- private static final int[] DEFAULT_EMPTY_ARRAY = {};
-
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
- * Creates a new array list with given capacity.
- *
- * @param capacity the initial capacity of the array list (may be 0).
- */
- public IntArrayList(final int capacity) {
- initArrayFromCapacity(capacity);
- }
-
- private void initArrayFromCapacity(final int capacity) {
- if (capacity < 0)
- throw new IllegalArgumentException("Initial capacity (" + capacity + ") is negative");
- if (capacity == 0)
- a = IntArrayList.DEFAULT_EMPTY_ARRAY;
- else
- a = new int[capacity];
- }
-
- public static int[] forceCapacity(final int[] array, final int length, final int preserve) {
- final int t[] = new int[length];
- System.arraycopy(array, 0, t, 0, preserve);
- return t;
- }
-
- /**
- * Grows this array list, ensuring that it can contain the given number of
- * entries without resizing,
- * and in case increasing the current capacity at least by a factor of 50%.
- *
- * @param capacity the new minimum capacity for this array list.
- */
-
- private void grow(int capacity) {
- if (capacity <= a.length)
- return;
- if (a != IntArrayList.DEFAULT_EMPTY_ARRAY)
- capacity = (int) Math.max(Math.min((long) a.length + (a.length >> 1), IntArrayList.MAX_ARRAY_SIZE),
- capacity);
- else if (capacity < DEFAULT_INITIAL_CAPACITY)
- capacity = DEFAULT_INITIAL_CAPACITY;
- a = IntArrayList.forceCapacity(a, capacity, size);
- assert size <= a.length;
- }
-
- public boolean add(final int k) {
- grow(size + 1);
- a[size++] = k;
- assert size <= a.length;
- return true;
- }
-
- public int getInt(final int index) {
- if (index >= size)
- throw new IndexOutOfBoundsException(
- "Index (" + index + ") is greater than or equal to list size (" + size + ")");
- return a[index];
- }
-
- }
- }
}
new file mode 100644
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2002-2026 Sebastiano Vigna
+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
+ *
+ * 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 java.util;
+
+import java.util.function.Consumer;
+
+/**
+ * Primitive int representation used by the bytecode-enhanced HashSet.
+ *
+ * <p>This implementation incorporates and adapts ideas and portions from
+ * fastutil OpenHashSet, with separate null and zero-key handling required by
+ * java.util.HashSet semantics.
+ */
+class IntHashSet extends PrimitiveHashSet {
+ int[] keys;
+
+ public IntHashSet() {
+ this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ public IntHashSet(final int initialCapacity, final float loadFactor) {
+ super(initialCapacity, loadFactor);
+ this.keys = new int[this.capacity + 1];
+ }
+
+ boolean addInt(int key) {
+ int pos;
+ if (key == 0) {
+ if (this.containsZero) {
+ return false;
+ }
+ this.containsZero = true;
+ } else {
+ int current;
+ final int[] curKeys = this.keys;
+ if (!((current = curKeys[pos = (int) IntHashSet.mixBits(key) & this.mask]) == 0)) {
+ if (current == key) {
+ return false;
+ }
+ while (!((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
+ if (current == key) {
+ return false;
+ }
+ }
+ }
+ curKeys[pos] = key;
+ }
+ if (this.size++ >= this.threshold) {
+ rehash(IntHashSet.leastArraySize(this.size + 1, this.loadFactor));
+ }
+ this.modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object key) {
+ if (key == null) {
+ return removeNull();
+ }
+ return key instanceof Integer && removeInt((Integer) key);
+ }
+
+ boolean removeInt(int key) {
+ if (key == 0) {
+ if (this.containsZero) {
+ return removeNullEntry();
+ }
+ return false;
+ }
+ int current;
+ final int[] curKeys = this.keys;
+ int pos;
+ if (((current = curKeys[pos = (int) IntHashSet.mixBits(key) & this.mask]) == 0)) {
+ return false;
+ }
+ if (current == key) {
+ return removeEntry(pos);
+ }
+ while (true) {
+ if (((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
+ return false;
+ }
+ if (current == key) {
+ return removeEntry(pos);
+ }
+ }
+ }
+
+ @Override
+ public boolean contains(Object key) {
+ if (key == null) {
+ return this.containsNull;
+ }
+ return key instanceof Integer && containsInt((Integer) key);
+ }
+
+ boolean containsInt(int key) {
+ if (key == 0) return this.containsZero;
+ int current;
+ final int[] curKeys = this.keys;
+ int pos = (int) IntHashSet.mixBits(key) & this.mask;
+ if ((current = curKeys[pos]) == 0) return false;
+ if (current == key) return true;
+ while ((current = curKeys[pos = (pos + 1) & this.mask]) != 0) {
+ if (current == key) return true;
+ }
+ return false;
+ }
+
+ Iterator<Integer> valueIterator() {
+ return new IntIterator();
+ }
+
+ @Override
+ public Spliterator<Number> spliterator() {
+ return new IntSpliterator(0, -1, 0, 0, false, false, false);
+ }
+
+ @Override
+ void forEachElement(Consumer<? super Number> action) {
+ if (containsZero) {
+ action.accept(Integer.valueOf(0));
+ }
+ final int[] curKeys = keys;
+ for (int i = capacity; i-- != 0;) {
+ int key = curKeys[i];
+ if (key != 0) {
+ action.accept(Integer.valueOf(key));
+ }
+ }
+ }
+
+ public void clear() {
+ if (this.size == 0) {
+ return;
+ }
+ this.size = 0;
+ this.containsZero = false;
+ this.containsNull = false;
+ Arrays.fill(this.keys, 0);
+ this.modCount++;
+ }
+
+ @Override
+ public IntHashSet clone() {
+ IntHashSet newSet = (IntHashSet) super.clone();
+ newSet.keys = this.keys.clone();
+ return newSet;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ for (int j = realSize(), i = 0; j-- != 0;) {
+ while (this.keys[i] == 0) {
+ i++;
+ }
+ h += this.keys[i];
+ i++;
+ }
+ return h;
+ }
+
+ private boolean removeEntry(int pos) {
+ this.size--;
+ shiftKeys(pos);
+ if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
+ && this.capacity > DEFAULT_INITIAL_CAPACITY) {
+ rehash(this.capacity >> 1);
+ }
+ this.modCount++;
+ return true;
+ }
+
+ private void shiftKeys(int pos) {
+ int last, slot;
+ int current;
+ final int[] curKeys = this.keys;
+ for (;;) {
+ pos = ((last = pos) + 1) & this.mask;
+ for (;;) {
+ if (((current = curKeys[pos]) == 0)) {
+ curKeys[last] = 0;
+ return;
+ }
+ slot = (int) IntHashSet.mixBits(current) & this.mask;
+ if (last <= pos ? (last >= slot || slot > pos) : (last >= slot && slot > pos)) {
+ break;
+ }
+ pos = (pos + 1) & this.mask;
+ }
+ curKeys[last] = current;
+ }
+ }
+
+ private boolean removeNullEntry() {
+ this.containsZero = false;
+ this.keys[this.capacity] = 0;
+ this.size--;
+ if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
+ && this.capacity > DEFAULT_INITIAL_CAPACITY) {
+ rehash(this.capacity >> 1);
+ }
+ this.modCount++;
+ return true;
+ }
+
+ private void rehash(final int newCapacity) {
+ final int[] oldKeys = this.keys;
+ final int newMask = newCapacity - 1;
+ final int[] newKeys = new int[newCapacity + 1];
+ int i = this.capacity, pos;
+ for (int j = realSize(); j-- != 0;) {
+ while ((oldKeys[--i] == 0))
+ ;
+ if (!((newKeys[pos = (int) IntHashSet.mixBits(oldKeys[i]) & newMask]) == 0)) {
+ while (!((newKeys[pos = (pos + 1) & newMask]) == 0))
+ ;
+ }
+ newKeys[pos] = oldKeys[i];
+ }
+ this.capacity = newCapacity;
+ this.mask = newMask;
+ this.threshold = IntHashSet.maxEntriesFill(this.capacity, this.loadFactor);
+ this.keys = newKeys;
+ }
+
+ private final class IntIterator implements PrimitiveIterator.OfInt {
+ private int position = IntHashSet.this.capacity;
+ private int last = -1;
+ private int counter = IntHashSet.this.size - (IntHashSet.this.containsNull ? 1 : 0);
+ private boolean mustReturnZero = IntHashSet.this.containsZero;
+ private IntArrayList wrapped;
+
+ @Override
+ public int nextInt() {
+ if (!hasNext()) {
+ throw new java.util.NoSuchElementException();
+ }
+ this.counter--;
+ if (this.mustReturnZero) {
+ this.mustReturnZero = false;
+ last = IntHashSet.this.capacity;
+ return IntHashSet.this.keys[IntHashSet.this.capacity];
+ }
+ final int[] curKeys = IntHashSet.this.keys;
+ for (;;) {
+ if (--this.position < 0) {
+ this.last = Integer.MIN_VALUE;
+ return this.wrapped.getInt(-this.position - 1);
+ }
+ if (!(curKeys[this.position] == 0)) {
+ return curKeys[this.last = this.position];
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.counter != 0;
+ }
+
+ private final void shiftKeys(int pos) {
+ int lastPos, slot;
+ int current;
+ final int[] curKeys = IntHashSet.this.keys;
+ for (;;) {
+ pos = ((lastPos = pos) + 1) & IntHashSet.this.mask;
+ for (;;) {
+ if (((current = curKeys[pos]) == 0)) {
+ curKeys[lastPos] = 0;
+ return;
+ }
+ slot = (int) IntHashSet.mixBits(current) & IntHashSet.this.mask;
+ if (lastPos <= pos ? (lastPos >= slot || slot > pos) : (lastPos >= slot && slot > pos)) {
+ break;
+ }
+ pos = (pos + 1) & IntHashSet.this.mask;
+ }
+ if (pos < lastPos) {
+ if (wrapped == null) {
+ wrapped = new IntArrayList(2);
+ }
+ wrapped.add(curKeys[pos]);
+ }
+ curKeys[lastPos] = current;
+ }
+ }
+
+ @Override
+ public void remove() {
+ if (this.last == -1) {
+ throw new IllegalStateException();
+ }
+ if (this.last == IntHashSet.this.capacity) {
+ IntHashSet.this.containsZero = false;
+ IntHashSet.this.keys[IntHashSet.this.capacity] = 0;
+ } else if (this.position >= 0) {
+ shiftKeys(this.last);
+ } else {
+ IntHashSet.this.remove(this.wrapped.getInt(-this.position - 1));
+ this.last = -1;
+ return;
+ }
+ IntHashSet.this.size--;
+ IntHashSet.this.modCount++;
+ this.last = -1;
+ }
+
+ }
+
+ private final class IntSpliterator implements Spliterator<Number> {
+ private int position;
+ private int fence;
+ private int expectedModCount;
+ private long estimatedSize;
+ private boolean nullPending;
+ private boolean zeroPending;
+ private boolean hasSplit;
+
+ IntSpliterator(int position, int fence, int expectedModCount,
+ long estimatedSize, boolean nullPending,
+ boolean zeroPending, boolean hasSplit) {
+ this.position = position;
+ this.fence = fence;
+ this.expectedModCount = expectedModCount;
+ this.estimatedSize = estimatedSize;
+ this.nullPending = nullPending;
+ this.zeroPending = zeroPending;
+ this.hasSplit = hasSplit;
+ }
+
+ private int getFence() {
+ if (fence < 0) {
+ expectedModCount = modCount;
+ estimatedSize = size;
+ nullPending = containsNull;
+ zeroPending = containsZero;
+ fence = capacity;
+ }
+ return fence;
+ }
+
+ private void checkForComodification() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public Spliterator<Number> trySplit() {
+ int high = getFence();
+ int low = position;
+ int middle = (low + high) >>> 1;
+ if (low >= middle) {
+ return null;
+ }
+ long splitEstimate = estimatedSize >>> 1;
+ IntSpliterator split = new IntSpliterator(low, middle,
+ expectedModCount, splitEstimate, nullPending,
+ zeroPending, true);
+ position = middle;
+ estimatedSize -= splitEstimate;
+ nullPending = false;
+ zeroPending = false;
+ hasSplit = true;
+ return split;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer<? super Number> action) {
+ Objects.requireNonNull(action);
+ int high = getFence();
+ if (nullPending) {
+ nullPending = false;
+ if (estimatedSize > 0) {
+ estimatedSize--;
+ }
+ action.accept(null);
+ checkForComodification();
+ return true;
+ }
+ if (zeroPending) {
+ zeroPending = false;
+ if (estimatedSize > 0) {
+ estimatedSize--;
+ }
+ action.accept(Integer.valueOf(0));
+ checkForComodification();
+ return true;
+ }
+ final int[] curKeys = keys;
+ while (position < high) {
+ int key = curKeys[position++];
+ if (key != 0) {
+ if (estimatedSize > 0) {
+ estimatedSize--;
+ }
+ action.accept(Integer.valueOf(key));
+ checkForComodification();
+ return true;
+ }
+ }
+ checkForComodification();
+ return false;
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super Number> action) {
+ Objects.requireNonNull(action);
+ int high = getFence();
+ if (nullPending) {
+ nullPending = false;
+ action.accept(null);
+ }
+ if (zeroPending) {
+ zeroPending = false;
+ action.accept(Integer.valueOf(0));
+ }
+ final int[] curKeys = keys;
+ while (position < high) {
+ int key = curKeys[position++];
+ if (key != 0) {
+ action.accept(Integer.valueOf(key));
+ }
+ }
+ estimatedSize = 0;
+ checkForComodification();
+ }
+
+ @Override
+ public long estimateSize() {
+ getFence();
+ return estimatedSize;
+ }
+
+ @Override
+ public int characteristics() {
+ return Spliterator.DISTINCT | (hasSplit ? 0 : Spliterator.SIZED);
+ }
+ }
+
+ private static class IntArrayList {
+ /** The initial default capacity of an array list. */
+ public static final int DEFAULT_INITIAL_CAPACITY = 10;
+ private static final int[] DEFAULT_EMPTY_ARRAY = {};
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /** The backing array. */
+ protected int a[];
+ /**
+ * The current actual size of the list (never greater than the backing-array
+ * length).
+ */
+ protected int size;
+
+ /**
+ * Creates a new array list with given capacity.
+ *
+ * @param capacity the initial capacity of the array list (may be 0).
+ */
+ public IntArrayList(final int capacity) {
+ initArrayFromCapacity(capacity);
+ }
+
+ private void initArrayFromCapacity(final int capacity) {
+ if (capacity < 0)
+ throw new IllegalArgumentException("Initial capacity (" + capacity + ") is negative");
+ if (capacity == 0)
+ a = IntArrayList.DEFAULT_EMPTY_ARRAY;
+ else
+ a = new int[capacity];
+ }
+
+ public static int[] forceCapacity(final int[] array, final int length, final int preserve) {
+ final int t[] = new int[length];
+ System.arraycopy(array, 0, t, 0, preserve);
+ return t;
+ }
+
+ /**
+ * Grows this array list, ensuring that it can contain the given number of
+ * entries without resizing,
+ * and in case increasing the current capacity at least by a factor of 50%.
+ *
+ * @param capacity the new minimum capacity for this array list.
+ */
+
+ private void grow(int capacity) {
+ if (capacity <= a.length)
+ return;
+ if (a != IntArrayList.DEFAULT_EMPTY_ARRAY)
+ capacity = (int) Math.max(Math.min((long) a.length + (a.length >> 1), IntArrayList.MAX_ARRAY_SIZE),
+ capacity);
+ else if (capacity < DEFAULT_INITIAL_CAPACITY)
+ capacity = DEFAULT_INITIAL_CAPACITY;
+ a = IntArrayList.forceCapacity(a, capacity, size);
+ assert size <= a.length;
+ }
+
+ public boolean add(final int k) {
+ grow(size + 1);
+ a[size++] = k;
+ assert size <= a.length;
+ return true;
+ }
+
+ public int getInt(final int index) {
+ if (index >= size)
+ throw new IndexOutOfBoundsException(
+ "Index (" + index + ") is greater than or equal to list size (" + size + ")");
+ return a[index];
+ }
+
+ }
+}
new file mode 100644
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2002-2026 Sebastiano Vigna
+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
+ *
+ * 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 java.util;
+
+import java.util.function.Consumer;
+
+/**
+ * Primitive long representation used by the bytecode-enhanced HashSet.
+ *
+ * <p>This implementation incorporates and adapts ideas and portions from
+ * fastutil OpenHashSet, with separate null and zero-key handling required by
+ * java.util.HashSet semantics.
+ */
+class LongHashSet extends PrimitiveHashSet {
+ long[] keys;
+
+ public LongHashSet() {
+ this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ public LongHashSet(final int initialCapacity, final float loadFactor) {
+ super(initialCapacity, loadFactor);
+ this.keys = new long[this.capacity + 1];
+ }
+
+ boolean addLong(long key) {
+ int pos;
+ if (key == 0L) {
+ if (this.containsZero) {
+ return false;
+ }
+ this.containsZero = true;
+ } else {
+ long current;
+ final long[] curKeys = this.keys;
+ if (!((current = curKeys[pos = (int) LongHashSet.mixBits(key) & this.mask]) == 0)) {
+ if (current == key) {
+ return false;
+ }
+ while (!((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
+ if (current == key) {
+ return false;
+ }
+ }
+ }
+ curKeys[pos] = key;
+ }
+ if (this.size++ >= this.threshold) {
+ rehash(LongHashSet.leastArraySize(this.size + 1, this.loadFactor));
+ }
+ this.modCount++;
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object key) {
+ if (key == null) {
+ return removeNull();
+ }
+ return key instanceof Long && removeLong((Long) key);
+ }
+
+ boolean removeLong(long key) {
+ if (key == 0L) {
+ if (this.containsZero) {
+ return removeNullEntry();
+ }
+ return false;
+ }
+ long current;
+ final long[] curKeys = this.keys;
+ int pos;
+ if (((current = curKeys[pos = (int) LongHashSet.mixBits(key) & this.mask]) == 0)) {
+ return false;
+ }
+ if (current == key) {
+ return removeEntry(pos);
+ }
+ while (true) {
+ if (((current = curKeys[pos = (pos + 1) & this.mask]) == 0)) {
+ return false;
+ }
+ if (current == key) {
+ return removeEntry(pos);
+ }
+ }
+ }
+
+ @Override
+ public boolean contains(Object key) {
+ if (key == null) {
+ return this.containsNull;
+ }
+ return key instanceof Long && containsLong((Long) key);
+ }
+
+ boolean containsLong(long key) {
+ if (key == 0L) return this.containsZero;
+ long current;
+ final long[] curKeys = this.keys;
+ int pos = (int) LongHashSet.mixBits(key) & this.mask;
+ if ((current = curKeys[pos]) == 0) return false;
+ if (current == key) return true;
+ while ((current = curKeys[pos = (pos + 1) & this.mask]) != 0) {
+ if (current == key) return true;
+ }
+ return false;
+ }
+
+
+ Iterator<Long> valueIterator() {
+ return new LongIterator();
+ }
+
+ @Override
+ public Spliterator<Number> spliterator() {
+ return new LongSpliterator(0, -1, 0, 0, false, false, false);
+ }
+
+ @Override
+ void forEachElement(Consumer<? super Number> action) {
+ if (containsZero) {
+ action.accept(Long.valueOf(0));
+ }
+ final long[] curKeys = keys;
+ for (int i = capacity; i-- != 0;) {
+ long key = curKeys[i];
+ if (key != 0) {
+ action.accept(Long.valueOf(key));
+ }
+ }
+ }
+
+ public void clear() {
+ if (this.size == 0) {
+ return;
+ }
+ this.size = 0;
+ this.containsZero = false;
+ this.containsNull = false;
+ Arrays.fill(this.keys, 0);
+ this.modCount++;
+ }
+
+ @Override
+ public LongHashSet clone() {
+ LongHashSet newSet = (LongHashSet) super.clone();
+ newSet.keys = this.keys.clone();
+ return newSet;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ for (int j = realSize(), i = 0; j-- != 0;) {
+ while (this.keys[i] == 0) {
+ i++;
+ }
+ h += (int) (this.keys[i] ^ (this.keys[i] >>> 32));
+ i++;
+ }
+ return h;
+ }
+
+ private boolean removeEntry(int pos) {
+ this.size--;
+ shiftKeys(pos);
+ if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
+ && this.capacity > DEFAULT_INITIAL_CAPACITY) {
+ rehash(this.capacity >> 1);
+ }
+ this.modCount++;
+ return true;
+ }
+
+ private void shiftKeys(int pos) {
+ int last, slot;
+ long current;
+ final long[] curKeys = this.keys;
+ for (;;) {
+ pos = ((last = pos) + 1) & this.mask;
+ for (;;) {
+ if (((current = curKeys[pos]) == 0)) {
+ curKeys[last] = 0;
+ return;
+ }
+ slot = (int) LongHashSet.mixBits(current) & this.mask;
+ if (last <= pos ? (last >= slot || slot > pos) : (last >= slot && slot > pos)) {
+ break;
+ }
+ pos = (pos + 1) & this.mask;
+ }
+ curKeys[last] = current;
+ }
+ }
+
+ private boolean removeNullEntry() {
+ this.containsZero = false;
+ this.keys[this.capacity] = 0;
+ this.size--;
+ if (this.capacity > this.minCapacity && this.size < this.threshold >> 2
+ && this.capacity > DEFAULT_INITIAL_CAPACITY) {
+ rehash(this.capacity >> 1);
+ }
+ this.modCount++;
+ return true;
+ }
+
+ private void rehash(final int newCapacity) {
+ final long[] oldKeys = this.keys;
+ final int newMask = newCapacity - 1;
+ final long[] newKeys = new long[newCapacity + 1];
+ int i = this.capacity, pos;
+ for (int j = realSize(); j-- != 0;) {
+ while ((oldKeys[--i] == 0))
+ ;
+ if (!((newKeys[pos = (int) LongHashSet.mixBits(oldKeys[i]) & newMask]) == 0)) {
+ while (!((newKeys[pos = (pos + 1) & newMask]) == 0))
+ ;
+ }
+ newKeys[pos] = oldKeys[i];
+ }
+ this.capacity = newCapacity;
+ this.mask = newMask;
+ this.threshold = LongHashSet.maxEntriesFill(this.capacity, this.loadFactor);
+ this.keys = newKeys;
+ }
+
+ private final class LongIterator implements PrimitiveIterator.OfLong {
+ private int position = LongHashSet.this.capacity;
+ private int last = -1;
+ private int counter = LongHashSet.this.size - (LongHashSet.this.containsNull ? 1 : 0);
+ private boolean mustReturnZero = LongHashSet.this.containsZero;
+ private LongArrayList wrapped;
+
+ @Override
+ public long nextLong() {
+ if (!hasNext()) {
+ throw new java.util.NoSuchElementException();
+ }
+ this.counter--;
+ if (this.mustReturnZero) {
+ this.mustReturnZero = false;
+ last = LongHashSet.this.capacity;
+ return LongHashSet.this.keys[LongHashSet.this.capacity];
+ }
+ final long[] curKeys = LongHashSet.this.keys;
+ for (;;) {
+ if (--this.position < 0) {
+ this.last = Integer.MIN_VALUE;
+ return this.wrapped.getLong(-this.position - 1);
+ }
+ if (!(curKeys[this.position] == 0)) {
+ return curKeys[this.last = this.position];
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.counter != 0;
+ }
+
+ private final void shiftKeys(int pos) {
+ int lastPos, slot;
+ long current;
+ final long[] curKeys = LongHashSet.this.keys;
+ for (;;) {
+ pos = ((lastPos = pos) + 1) & LongHashSet.this.mask;
+ for (;;) {
+ if (((current = curKeys[pos]) == 0)) {
+ curKeys[lastPos] = 0;
+ return;
+ }
+ slot = (int) LongHashSet.mixBits(current) & LongHashSet.this.mask;
+ if (lastPos <= pos ? (lastPos >= slot || slot > pos) : (lastPos >= slot && slot > pos)) {
+ break;
+ }
+ pos = (pos + 1) & LongHashSet.this.mask;
+ }
+ if (pos < lastPos) {
+ if (wrapped == null) {
+ wrapped = new LongArrayList(2);
+ }
+ wrapped.add(curKeys[pos]);
+ }
+ curKeys[lastPos] = current;
+ }
+ }
+
+ @Override
+ public void remove() {
+ if (this.last == -1) {
+ throw new IllegalStateException();
+ }
+ if (this.last == LongHashSet.this.capacity) {
+ LongHashSet.this.containsZero = false;
+ LongHashSet.this.keys[LongHashSet.this.capacity] = 0;
+ } else if (this.position >= 0) {
+ shiftKeys(this.last);
+ } else {
+ LongHashSet.this.remove(this.wrapped.getLong(-this.position - 1));
+ this.last = -1;
+ return;
+ }
+ LongHashSet.this.size--;
+ LongHashSet.this.modCount++;
+ this.last = -1;
+ }
+
+ }
+
+ private final class LongSpliterator implements Spliterator<Number> {
+ private int position;
+ private int fence;
+ private int expectedModCount;
+ private long estimatedSize;
+ private boolean nullPending;
+ private boolean zeroPending;
+ private boolean hasSplit;
+
+ LongSpliterator(int position, int fence, int expectedModCount,
+ long estimatedSize, boolean nullPending,
+ boolean zeroPending, boolean hasSplit) {
+ this.position = position;
+ this.fence = fence;
+ this.expectedModCount = expectedModCount;
+ this.estimatedSize = estimatedSize;
+ this.nullPending = nullPending;
+ this.zeroPending = zeroPending;
+ this.hasSplit = hasSplit;
+ }
+
+ private int getFence() {
+ if (fence < 0) {
+ expectedModCount = modCount;
+ estimatedSize = size;
+ nullPending = containsNull;
+ zeroPending = containsZero;
+ fence = capacity;
+ }
+ return fence;
+ }
+
+ private void checkForComodification() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public Spliterator<Number> trySplit() {
+ int high = getFence();
+ int low = position;
+ int middle = (low + high) >>> 1;
+ if (low >= middle) {
+ return null;
+ }
+ long splitEstimate = estimatedSize >>> 1;
+ LongSpliterator split = new LongSpliterator(low, middle,
+ expectedModCount, splitEstimate, nullPending,
+ zeroPending, true);
+ position = middle;
+ estimatedSize -= splitEstimate;
+ nullPending = false;
+ zeroPending = false;
+ hasSplit = true;
+ return split;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer<? super Number> action) {
+ Objects.requireNonNull(action);
+ int high = getFence();
+ if (nullPending) {
+ nullPending = false;
+ if (estimatedSize > 0) {
+ estimatedSize--;
+ }
+ action.accept(null);
+ checkForComodification();
+ return true;
+ }
+ if (zeroPending) {
+ zeroPending = false;
+ if (estimatedSize > 0) {
+ estimatedSize--;
+ }
+ action.accept(Long.valueOf(0L));
+ checkForComodification();
+ return true;
+ }
+ final long[] curKeys = keys;
+ while (position < high) {
+ long key = curKeys[position++];
+ if (key != 0L) {
+ if (estimatedSize > 0) {
+ estimatedSize--;
+ }
+ action.accept(Long.valueOf(key));
+ checkForComodification();
+ return true;
+ }
+ }
+ checkForComodification();
+ return false;
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super Number> action) {
+ Objects.requireNonNull(action);
+ int high = getFence();
+ if (nullPending) {
+ nullPending = false;
+ action.accept(null);
+ }
+ if (zeroPending) {
+ zeroPending = false;
+ action.accept(Long.valueOf(0L));
+ }
+ final long[] curKeys = keys;
+ while (position < high) {
+ long key = curKeys[position++];
+ if (key != 0L) {
+ action.accept(Long.valueOf(key));
+ }
+ }
+ estimatedSize = 0;
+ checkForComodification();
+ }
+
+ @Override
+ public long estimateSize() {
+ getFence();
+ return estimatedSize;
+ }
+
+ @Override
+ public int characteristics() {
+ return Spliterator.DISTINCT | (hasSplit ? 0 : Spliterator.SIZED);
+ }
+ }
+
+ private static class LongArrayList {
+ /** The initial default capacity of an array list. */
+ public static final int DEFAULT_INITIAL_CAPACITY = 10;
+ private static final long[] DEFAULT_EMPTY_ARRAY = {};
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /** The backing array. */
+ protected long a[];
+ /**
+ * The current actual size of the list (never greater than the backing-array
+ * length).
+ */
+ protected int size;
+
+ /**
+ * Creates a new array list with given capacity.
+ *
+ * @param capacity the initial capacity of the array list (may be 0).
+ */
+ public LongArrayList(final int capacity) {
+ initArrayFromCapacity(capacity);
+ }
+
+ private void initArrayFromCapacity(final int capacity) {
+ if (capacity < 0)
+ throw new IllegalArgumentException("Initial capacity (" + capacity + ") is negative");
+ if (capacity == 0)
+ a = LongArrayList.DEFAULT_EMPTY_ARRAY;
+ else
+ a = new long[capacity];
+ }
+
+ public static long[] forceCapacity(final long[] array, final int length, final int preserve) {
+ final long t[] = new long[length];
+ System.arraycopy(array, 0, t, 0, preserve);
+ return t;
+ }
+
+ /**
+ * Grows this array list, ensuring that it can contain the given number of
+ * entries without resizing,
+ * and in case increasing the current capacity at least by a factor of 50%.
+ *
+ * @param capacity the new minimum capacity for this array list.
+ */
+
+ private void grow(int capacity) {
+ if (capacity <= a.length)
+ return;
+ if (a != LongArrayList.DEFAULT_EMPTY_ARRAY)
+ capacity = (int) Math.max(Math.min((long) a.length + (a.length >> 1), LongArrayList.MAX_ARRAY_SIZE),
+ capacity);
+ else if (capacity < DEFAULT_INITIAL_CAPACITY)
+ capacity = DEFAULT_INITIAL_CAPACITY;
+ a = LongArrayList.forceCapacity(a, capacity, size);
+ assert size <= a.length;
+ }
+
+ public boolean add(final long k) {
+ grow(size + 1);
+ a[size++] = k;
+ assert size <= a.length;
+ return true;
+ }
+
+ public long getLong(final int index) {
+ if (index >= size)
+ throw new IndexOutOfBoundsException(
+ "Index (" + index + ") is greater than or equal to list size (" + size + ")");
+ return a[index];
+ }
+
+ }
+}
new file mode 100644
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2002-2026 Sebastiano Vigna
+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
+ *
+ * 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 java.util;
+
+import java.util.function.Consumer;
+
+/**
+ * Common contract for the primitive-specialized HashSet representations.
+ *
+ * <p>This implementation incorporates and adapts ideas and portions from
+ * fastutil OpenHashSet. It is specialized for the bytecode-enhanced
+ * java.util.HashSet implementation in BiSheng JDK.
+ */
+abstract class PrimitiveHashSet extends AbstractSet<Number> implements Cloneable {
+ static final long LONG_PHI = 0x9E3779B97F4A7C15L;
+
+ static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ static final float DEFAULT_LOAD_FACTOR = .75f;
+
+ int mask;
+
+ boolean containsNull;
+
+ boolean containsZero;
+
+ int capacity;
+
+ int threshold;
+
+ int minCapacity;
+
+ int size;
+
+ int modCount;
+
+ float loadFactor;
+
+ // Retained only to emit the standard HashSet serialization metadata.
+ // It is intentionally independent of this table's occupancy factor.
+ float hashMapLoadFactor = HashMap.DEFAULT_LOAD_FACTOR;
+
+ public PrimitiveHashSet(final int initialCapacity, final float loadFactor) {
+ if (loadFactor <= 0 || Float.isNaN(loadFactor) || loadFactor >= 1) {
+ throw new IllegalArgumentException("Load factor must be between 0 and 1");
+ }
+ if (initialCapacity < 0) {
+ throw new IllegalArgumentException("Initial capacity must be non-negative");
+ }
+ this.loadFactor = loadFactor;
+ this.minCapacity = this.capacity = PrimitiveHashSet.leastArraySize(initialCapacity, loadFactor);
+ this.mask = this.capacity - 1;
+ this.threshold = PrimitiveHashSet.maxEntriesFill(this.capacity, loadFactor);
+ }
+
+ protected int realSize() {
+ return this.size - (this.containsZero ? 1 : 0) - (this.containsNull ? 1 : 0);
+ }
+
+ protected static long mixBits(final long n) {
+ long res = n * LONG_PHI;
+ res ^= res >>> 32;
+ return res ^ (res >>> 16);
+ }
+
+ protected static int maxEntriesFill(int capacity, float loadFactor) {
+ return Math.min((int) Math.ceil(capacity * loadFactor), capacity - 1);
+ }
+
+ protected static int leastArraySize(int expectedElements, float loadFactor) {
+ final long size = Math.max(2, nextPowerOfTwo((long) Math.ceil(expectedElements / loadFactor)));
+ if (size > (1 << 30)) {
+ throw new IllegalArgumentException("Expected number of elements is too large: " + expectedElements
+ + " with load factor " + loadFactor);
+ }
+ return (int) size;
+ }
+
+ protected static long nextPowerOfTwo(long n) {
+ if (n == 0) {
+ return 1;
+ }
+ n--;
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ n |= n >> 16;
+ return (n | n >> 32) + 1;
+ }
+
+ @Override
+ public int size() {
+ return this.size;
+ }
+
+ public boolean isEmpty() {
+ return this.size == 0;
+ }
+
+
+ abstract Iterator<? extends Number> valueIterator();
+
+ abstract void forEachElement(Consumer<? super Number> action);
+
+ final boolean addNull() {
+ if (containsNull) {
+ return false;
+ }
+ containsNull = true;
+ size++;
+ modCount++;
+ return true;
+ }
+
+ final boolean removeNull() {
+ if (!containsNull) {
+ return false;
+ }
+ containsNull = false;
+ size--;
+ modCount++;
+ return true;
+ }
+
+ final void invalidateIterators() {
+ modCount++;
+ }
+
+ @Override
+ public final Iterator<Number> iterator() {
+ return new Iterator<>() {
+ private final Iterator<? extends Number> values = valueIterator();
+ private int expectedModCount = modCount;
+ private boolean nullPending = containsNull;
+ private boolean canRemove;
+ private boolean lastWasNull;
+
+ private void checkForComodification() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nullPending || values.hasNext();
+ }
+
+ @Override
+ public Number next() {
+ checkForComodification();
+ if (nullPending) {
+ nullPending = false;
+ canRemove = true;
+ lastWasNull = true;
+ return null;
+ }
+ Number value = values.next();
+ canRemove = true;
+ lastWasNull = false;
+ return value;
+ }
+
+ @Override
+ public void remove() {
+ if (!canRemove) {
+ throw new IllegalStateException();
+ }
+ checkForComodification();
+ if (lastWasNull) {
+ removeNull();
+ } else {
+ values.remove();
+ }
+ expectedModCount = modCount;
+ canRemove = false;
+ }
+ };
+ }
+
+ @Override
+ public final void forEach(Consumer<? super Number> action) {
+ Objects.requireNonNull(action);
+ int expectedModCount = modCount;
+ if (containsNull) {
+ action.accept(null);
+ }
+ forEachElement(action);
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public abstract boolean contains(Object e);
+
+ @Override
+ public abstract boolean remove(Object e);
+
+ public PrimitiveHashSet clone() {
+ try {
+ PrimitiveHashSet cloned = (PrimitiveHashSet) super.clone();
+ return cloned;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError(e);
+ }
+ }
+}
+