Add back IntObjectMap.values(Class<V>)
Motivation: Although the new IntObjectMap.values() that returns Collection is useful, the removed values(Class<V>) that returns an array is also useful. It's also good for backward compatibility. Modifications: - Add IntObjectMap.values(Class<V>) back - Miscellaneous improvements - Cache the collection returned by IntObjectHashMap.values() - Inspector warnings - Update the IntObjectHashMapTest to test both values() Result: - Backward compatibility - Potential performance improvement of values()
This commit is contained in:
parent
b9f575352b
commit
759a2b9abe
@ -15,6 +15,7 @@
|
||||
|
||||
package io.netty.util.collection;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -51,6 +52,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
|
||||
private int[] keys;
|
||||
private V[] values;
|
||||
private Collection<V> valueCollection;
|
||||
private int size;
|
||||
|
||||
public IntObjectHashMap() {
|
||||
@ -78,7 +80,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
|
||||
// Allocate the arrays.
|
||||
keys = new int[capacity];
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
|
||||
V[] temp = (V[]) new Object[capacity];
|
||||
values = temp;
|
||||
|
||||
@ -190,9 +192,9 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
@Override
|
||||
public boolean containsValue(V value) {
|
||||
V v = toInternal(value);
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
for (V value1 : values) {
|
||||
// The map supports null values; this will be matched as NULL_VALUE.equals(NULL_VALUE).
|
||||
if (values[i] != null && values[i].equals(v)) {
|
||||
if (value1 != null && value1.equals(v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -221,9 +223,24 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
return outKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V[] values(Class<V> clazz) {
|
||||
@SuppressWarnings("unchecked")
|
||||
V[] outValues = (V[]) Array.newInstance(clazz, size());
|
||||
int targetIx = 0;
|
||||
for (V value : values) {
|
||||
if (value != null) {
|
||||
outValues[targetIx++] = value;
|
||||
}
|
||||
}
|
||||
return outValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return new AbstractCollection<V>() {
|
||||
Collection<V> valueCollection = this.valueCollection;
|
||||
if (valueCollection == null) {
|
||||
this.valueCollection = valueCollection = new AbstractCollection<V>() {
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return new Iterator<V>() {
|
||||
@ -252,13 +269,16 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
};
|
||||
}
|
||||
|
||||
return valueCollection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Hashcode is based on all non-zero, valid keys. We have to scan the whole keys
|
||||
// array, which may have different lengths for two maps of same size(), so the
|
||||
// capacity cannot be used as input for hashing but the size can.
|
||||
int hash = size;
|
||||
for (int i = 0; i < keys.length; ++i) {
|
||||
for (int key : keys) {
|
||||
// 0 can be a valid key or unused slot, but won't impact the hashcode in either case.
|
||||
// This way we can use a cheap loop without conditionals, or hard-to-unroll operations,
|
||||
// or the devastatingly bad memory locality of visiting value objects.
|
||||
@ -266,7 +286,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
// of terms, only their values; since the map is an unordered collection and
|
||||
// entries can end up in different positions in different maps that have the same
|
||||
// elements, but with different history of puts/removes, due to conflicts.
|
||||
hash ^= keys[i];
|
||||
hash ^= key;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@ -380,8 +400,8 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
int nextFree = index;
|
||||
for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
|
||||
int bucket = hashIndex(keys[i]);
|
||||
if ((i < bucket && (bucket <= nextFree || nextFree <= i))
|
||||
|| (bucket <= nextFree && nextFree <= i)) {
|
||||
if (i < bucket && (bucket <= nextFree || nextFree <= i) ||
|
||||
bucket <= nextFree && nextFree <= i) {
|
||||
// Move the displaced entry "back" to the first available position.
|
||||
keys[nextFree] = keys[i];
|
||||
values[nextFree] = values[i];
|
||||
@ -412,7 +432,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
V[] oldVals = values;
|
||||
|
||||
keys = new int[newCapacity];
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
|
||||
V[] temp = (V[]) new Object[newCapacity];
|
||||
values = temp;
|
||||
|
||||
@ -425,8 +445,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
||||
// Inlined put(), but much simpler: we don't need to worry about
|
||||
// duplicated keys, growing/rehashing, or failing to insert.
|
||||
int oldKey = oldKeys[i];
|
||||
int startIndex = hashIndex(oldKey);
|
||||
int index = startIndex;
|
||||
int index = hashIndex(oldKey);
|
||||
|
||||
for (;;) {
|
||||
if (values[index] == null) {
|
||||
|
@ -114,5 +114,10 @@ public interface IntObjectMap<V> {
|
||||
/**
|
||||
* Gets the values contained in this map.
|
||||
*/
|
||||
V[] values(Class<V> clazz);
|
||||
|
||||
/**
|
||||
* Gets the values contatins in this map as a {@link Collection}.
|
||||
*/
|
||||
Collection<V> values();
|
||||
}
|
||||
|
@ -277,19 +277,28 @@ public class IntObjectHashMapTest {
|
||||
map.put(4, new Value("v4"));
|
||||
map.remove(4);
|
||||
|
||||
Collection<Value> values = map.values();
|
||||
assertEquals(3, values.size());
|
||||
|
||||
// Ensure values() return all values.
|
||||
Set<Value> expected = new HashSet<Value>();
|
||||
Set<Value> actual = new HashSet<Value>();
|
||||
|
||||
expected.add(v1);
|
||||
expected.add(v2);
|
||||
expected.add(v3);
|
||||
|
||||
Set<Value> found = new HashSet<Value>();
|
||||
for (Value value : values) {
|
||||
assertTrue(found.add(value));
|
||||
Value[] valueArray = map.values(Value.class);
|
||||
assertEquals(3, valueArray.length);
|
||||
for (Value value : valueArray) {
|
||||
assertTrue(actual.add(value));
|
||||
}
|
||||
assertEquals(expected, found);
|
||||
assertEquals(expected, actual);
|
||||
actual.clear();
|
||||
|
||||
Collection<Value> valueCollection = map.values();
|
||||
assertEquals(3, valueCollection.size());
|
||||
for (Value value : valueCollection) {
|
||||
assertTrue(actual.add(value));
|
||||
}
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -315,14 +324,14 @@ public class IntObjectHashMapTest {
|
||||
map2.put(key, key);
|
||||
}
|
||||
assertEquals(map1.hashCode(), map2.hashCode());
|
||||
assertTrue(map1.equals(map2));
|
||||
assertEquals(map1, map2);
|
||||
// Remove one "middle" element, maps should now be non-equals.
|
||||
int[] keys = map1.keys();
|
||||
map2.remove(keys[50]);
|
||||
assertFalse(map1.equals(map2));
|
||||
// Put it back; will likely be in a different position, but maps will be equal again.
|
||||
map2.put(keys[50], map1.keys()[50]);
|
||||
assertTrue(map1.equals(map2));
|
||||
assertEquals(map1, map2);
|
||||
assertEquals(map1.hashCode(), map2.hashCode());
|
||||
// Make map2 have one extra element, will be non-equal.
|
||||
map2.put(1000, 1000);
|
||||
@ -336,7 +345,7 @@ public class IntObjectHashMapTest {
|
||||
map2.put(key, key);
|
||||
}
|
||||
assertEquals(map1.hashCode(), map2.hashCode());
|
||||
assertTrue(map1.equals(map2));
|
||||
assertEquals(map1, map2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user