Skip to content
9 changes: 7 additions & 2 deletions test/jdk/TEST.groups
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,14 @@ jdk_util = \
:jdk_stream

valhalla_adopted = \
java/util/Collections/AddAll.java \
java/util/Arrays \
java/util/Collections
java/util/Collections \
java/util/HashMap \
java/util/HashSet \
java/util/Hashtable \
java/util/LinkedHashMap \
java/util/LinkedHashSet \
java/util/LinkedList

# All util components not part of some other util category
jdk_util_other = \
Expand Down
17 changes: 13 additions & 4 deletions test/jdk/java/util/HashMap/KeySetRemove.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,19 +26,28 @@
* @bug 4286765
* @summary HashMap and TreeMap entrySet().remove(k) spuriously returned
* false if the Map previously mapped k to null.
* @library /test/lib
*/

import jdk.test.lib.valueclass.VClass;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class KeySetRemove {
public static void main(String[] args) throws Exception {
Map[] m = {new HashMap(), new TreeMap()};
for (int i=0; i<m.length; i++) {
Map[] m = { new HashMap(), new TreeMap() };
for (int i = 0; i < m.length; i++) {
m[i].put("bananas", null);
if (!m[i].keySet().remove("bananas"))
throw new Exception("Yes, we have no bananas: "+i);
throw new Exception("Yes, we have no bananas: " + i);
}

Map[] valueMaps = { new HashMap<>(), new TreeMap<>() };
for (int i = 0; i < valueMaps.length; i++) {
valueMaps[i].put(new VClass(1, new int[] { 1 }), null);
if (!valueMaps[i].keySet().remove(new VClass(1, new int[] { 1 })))
throw new Exception("Value banana was not removed: " + i);
}
}
}
12 changes: 10 additions & 2 deletions test/jdk/java/util/HashMap/NullKeyAtResize.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -27,19 +27,27 @@
* @summary If the key to be inserted into a HashMap is null and the table
* needs to be resized as part of the insertion then addEntry will try to
* recalculate the hash of a null key. This will fail with an NPE.
* @library /test/lib
*/

import jdk.test.lib.valueclass.VClass;
import java.util.*;
import java.util.function.IntFunction;

public class NullKeyAtResize {
public static void main(String[] args) throws Exception {
test(Integer::valueOf);
test(i -> new VClass(i, new int[] { i }));
}

private static void test(IntFunction<Object> keyFactory) throws Exception {
List<Object> old_order = new ArrayList<>();
Map<Object,Object> m = new HashMap<>(16);
int number = 0;
while(number < 100000) {
m.put(null,null); // try to put in null. This may cause resize.
m.remove(null); // remove it.
Integer adding = (number += 100);
Object adding = keyFactory.apply(number += 100);
m.put(adding, null); // try to put in a number. This wont cause resize.
List<Object> new_order = new ArrayList<>();
new_order.addAll(m.keySet());
Expand Down
53 changes: 52 additions & 1 deletion test/jdk/java/util/HashMap/PutNullKey.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,8 +26,10 @@
* @bug 8046085
* @summary Ensure that when trees are being used for collisions that null key
* insertion still works.
* @library /test/lib
*/

import jdk.test.lib.valueclass.AsValueClass;
import java.util.*;
import java.util.stream.IntStream;

Expand Down Expand Up @@ -78,7 +80,46 @@ public int compareTo(CollidingHash o) {
}
}

@AsValueClass
public static class CollidingHashValue implements Comparable<CollidingHashValue> {

private final int value;

public CollidingHashValue(int value) {
this.value = value;
}

@Override
public int hashCode() {
// intentionally bad hashcode. Force into first bin.
return 0;
}

@Override
public boolean equals(Object o) {
if (null == o) {
return false;
}

if (o.getClass() != CollidingHashValue.class) {
return false;
}

return value == ((CollidingHashValue) o).value;
}

@Override
public int compareTo(CollidingHashValue o) {
return value - o.value;
}
}

public static void main(String[] args) throws Exception {
testCollidingHash();
testCollidingHashValue();
}

private static void testCollidingHash() {
Map<Object,Object> m = new HashMap<>(INITIAL_CAPACITY, LOAD_FACTOR);
IntStream.range(0, SIZE)
.mapToObj(CollidingHash::new)
Expand All @@ -87,4 +128,14 @@ public static void main(String[] args) throws Exception {
// kaboom?
m.put(null, null);
}

private static void testCollidingHashValue() {
Map<Object,Object> m = new HashMap<>(INITIAL_CAPACITY, LOAD_FACTOR);
IntStream.range(0, SIZE)
.mapToObj(CollidingHashValue::new)
.forEach(e -> { m.put(e, e); });

// kaboom?
m.put(null, null);
}
}
53 changes: 52 additions & 1 deletion test/jdk/java/util/HashMap/ReplaceExisting.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -27,9 +27,11 @@
* @summary Verify that replacing the value for an existing key does not
* corrupt active iterators, in particular due to a resize() occurring and
* not updating modCount.
* @library /test/lib
* @run main ReplaceExisting
*/

import jdk.test.lib.valueclass.VClass;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -43,6 +45,8 @@ public static void main(String[] args) {
for (int i = 0; i <= ENTRIES; i++) {
HashMap<Integer,Integer> hm = prepHashMap();
testItr(hm, i);
HashMap<VClass,Integer> tupleHm = prepTupleHashMap();
testTupleItr(tupleHm, i);
}
}

Expand All @@ -56,6 +60,15 @@ private static HashMap<Integer,Integer> prepHashMap() {
return hm;
}

private static HashMap<VClass,Integer> prepTupleHashMap() {
HashMap<VClass,Integer> hm = new HashMap<>(16, 0.75f);
// Add items to one more than the resize threshold
for (int i = 0; i < ENTRIES; i++) {
hm.put(new VClass(i * 10, new int[] { i * 10 }), i * 10);
}
return hm;
}

/* Iterate hm for elemBeforePut elements, then call put() to replace value
* for existing key. With bug 8025173, this will also cause a resize, but
* not increase the modCount.
Expand Down Expand Up @@ -98,4 +111,42 @@ private static void testItr(HashMap<Integer,Integer> hm, int elemBeforePut) {
throw new RuntimeException("Collected keys do not match original set of keys");
}
}

private static void testTupleItr(HashMap<VClass,Integer> hm, int elemBeforePut) {
if (elemBeforePut > hm.size()) {
throw new IllegalArgumentException("Error in test: elemBeforePut must be <= HashMap size");
}
// Create a copy of the keys
HashSet<VClass> keys = new HashSet<>(hm.size());
keys.addAll(hm.keySet());

HashSet<VClass> collected = new HashSet<>(hm.size());

// Run itr for elemBeforePut items, collecting returned elems
Iterator<VClass> itr = hm.keySet().iterator();
for (int i = 0; i < elemBeforePut; i++) {
VClass retVal = itr.next();
if (!collected.add(retVal)) {
throw new RuntimeException("Corrupt iterator: key " + retVal + " already encountered");
}
}

// Do put() to replace entry (and resize table when bug present)
if (null == hm.put(new VClass(0, new int[] { 0 }), 100)) {
throw new RuntimeException("Error in test: expected key (0, 0) to be in the HashMap");
}

// Finish itr + collecting returned elems
while(itr.hasNext()) {
VClass retVal = itr.next();
if (!collected.add(retVal)) {
throw new RuntimeException("Corrupt iterator: key " + retVal + " already encountered");
}
}

// Compare returned elems to original copy of keys
if (!keys.equals(collected)) {
throw new RuntimeException("Collected keys do not match original set of keys");
}
}
}
20 changes: 19 additions & 1 deletion test/jdk/java/util/HashMap/SetValue.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,8 +26,10 @@
* @bug 4627516
* @summary HashMap.Entry.setValue() returns new value (as opposed to old)
* @author jbloch
* @library /test/lib
*/

import jdk.test.lib.valueclass.VClass;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -37,11 +39,27 @@ public class SetValue {
static final String newValue = "new";

public static void main(String[] args) throws Exception {
testStringValue();
testVClass();
}

private static void testStringValue() {
Map m = new HashMap();
m.put(key, oldValue);
Map.Entry e = (Map.Entry) m.entrySet().iterator().next();
Object returnVal = e.setValue(newValue);
if (!returnVal.equals(oldValue))
throw new RuntimeException("Return value: " + returnVal);
}

private static void testVClass() {
Map<String, VClass> m = new HashMap<>();
VClass oldValue = new VClass(1, new int[] { 1 });
VClass newValue = new VClass(2, new int[] { 2 });
m.put(key, oldValue);
Map.Entry<String, VClass> e = m.entrySet().iterator().next();
Object returnVal = e.setValue(newValue);
if (!returnVal.equals(oldValue))
throw new RuntimeException("Return value: " + returnVal);
}
}
57 changes: 49 additions & 8 deletions test/jdk/java/util/HashMap/ToArray.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -31,27 +31,26 @@
import java.util.List;
import java.util.Map;
import java.util.stream.LongStream;
import jdk.test.lib.valueclass.VClass;

/*
* @test
* @bug 8336669
* @summary HashMap.toArray() behavior tests
* @author tvaleev
* @library /test/lib
*/
public class ToArray {
// An interned identity class holding an int (like non-Preview Integer)
record Int(int intValue) implements Comparable<Int> {
@Override
public int compareTo(Int o) {
return Integer.compare(intValue, o.intValue);
}
}

public static void main(String[] args) {
checkMap(false);
checkMap(true);
checkSet(false);
checkSet(true);
checkVClassMap(false);
checkVClassMap(true);
checkVClassSet(false);
checkVClassSet(true);
}

private static <T extends Comparable<T>> void checkToArray(String message, T[] expected, Collection<T> collection,
Expand Down Expand Up @@ -161,4 +160,46 @@ private static void checkSet(boolean ordered) {
checkToArray("Collisions", LongStream.range(0, 100).mapToObj(x -> x | (x << 32))
.toArray(Long[]::new), longSet, !ordered);
}

private static void checkVClassMap(boolean ordered) {
Map<VClass, VClass> map = ordered ? new LinkedHashMap<>() : new HashMap<>();
checkToArray("Empty-tuple-keys", new VClass[0], map.keySet(), !ordered);
checkToArray("Empty-tuple-values", new VClass[0], map.values(), !ordered);

List<VClass> keys = new ArrayList<>();
List<VClass> values = new ArrayList<>();
for (int i = 0; i < 100; i++) {
keys.add(new VClass(i, new int[] { i }));
values.add(new VClass(i * 2, new int[] { i * 2 }));
map.put(new VClass(i, new int[] { i }), new VClass(i * 2, new int[] { i * 2 }));
checkToArray(i + "-tuple-keys", keys.toArray(new VClass[0]), map.keySet(), !ordered);
checkToArray(i + "-tuple-values", values.toArray(new VClass[0]), map.values(), !ordered);
}
map.clear();
checkToArray("Empty-tuple-keys", new VClass[0], map.keySet(), !ordered);
checkToArray("Empty-tuple-values", new VClass[0], map.values(), !ordered);
}

private static void checkVClassSet(boolean ordered) {
Collection<VClass> set = ordered ? new LinkedHashSet<>() : new HashSet<>();
checkToArray("Empty-tuple", new VClass[0], set, !ordered);
set.add(new VClass(1, new int[] { 1 }));
checkToArray("One-tuple", new VClass[]{new VClass(1, new int[] { 1 })}, set, !ordered);
set.add(new VClass(2, new int[] { 2 }));
checkToArray("Two-tuple", new VClass[]{new VClass(1, new int[] { 1 }), new VClass(2, new int[] { 2 })}, set, !ordered);

Collection<VClass> tupleSet = ordered ? new LinkedHashSet<>() : new HashSet<>();
for (int x = 0; x < 100; x++) {
tupleSet.add(new VClass(x, new int[] { x }));
}
checkToArray("100-tuple", LongStream.range(0, 100).mapToObj(x -> new VClass((int) x, new int[] { (int) x }))
.toArray(VClass[]::new), tupleSet, !ordered);
tupleSet.clear();
checkToArray("After-clear-tuple", new VClass[0], tupleSet, !ordered);
for (int x = 0; x < 100; x++) {
tupleSet.add(new VClass(x, new int[] { -31 * x }));
}
checkToArray("Collisions-tuple", LongStream.range(0, 100).mapToObj(x -> new VClass((int) x, new int[] { -31 * (int) x }))
.toArray(VClass[]::new), tupleSet, !ordered);
}
}
Loading