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
338b60821d
commit
040c340f76
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
package io.netty.util.collection;
|
package io.netty.util.collection;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.util.AbstractCollection;
|
import java.util.AbstractCollection;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -51,6 +52,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
|
|
||||||
private int[] keys;
|
private int[] keys;
|
||||||
private V[] values;
|
private V[] values;
|
||||||
|
private Collection<V> valueCollection;
|
||||||
private int size;
|
private int size;
|
||||||
|
|
||||||
public IntObjectHashMap() {
|
public IntObjectHashMap() {
|
||||||
@ -78,7 +80,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
|
|
||||||
// Allocate the arrays.
|
// Allocate the arrays.
|
||||||
keys = new int[capacity];
|
keys = new int[capacity];
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
|
||||||
V[] temp = (V[]) new Object[capacity];
|
V[] temp = (V[]) new Object[capacity];
|
||||||
values = temp;
|
values = temp;
|
||||||
|
|
||||||
@ -190,9 +192,9 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
@Override
|
@Override
|
||||||
public boolean containsValue(V value) {
|
public boolean containsValue(V value) {
|
||||||
V v = toInternal(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).
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,9 +223,24 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
return outKeys;
|
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
|
@Override
|
||||||
public Collection<V> values() {
|
public Collection<V> values() {
|
||||||
return new AbstractCollection<V>() {
|
Collection<V> valueCollection = this.valueCollection;
|
||||||
|
if (valueCollection == null) {
|
||||||
|
this.valueCollection = valueCollection = new AbstractCollection<V>() {
|
||||||
@Override
|
@Override
|
||||||
public Iterator<V> iterator() {
|
public Iterator<V> iterator() {
|
||||||
return new Iterator<V>() {
|
return new Iterator<V>() {
|
||||||
@ -252,13 +269,16 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return valueCollection;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
// Hashcode is based on all non-zero, valid keys. We have to scan the whole keys
|
// 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
|
// 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.
|
// capacity cannot be used as input for hashing but the size can.
|
||||||
int hash = size;
|
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.
|
// 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,
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// elements, but with different history of puts/removes, due to conflicts.
|
||||||
hash ^= keys[i];
|
hash ^= key;
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@ -380,8 +400,8 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
int nextFree = index;
|
int nextFree = index;
|
||||||
for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
|
for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
|
||||||
int bucket = hashIndex(keys[i]);
|
int bucket = hashIndex(keys[i]);
|
||||||
if ((i < bucket && (bucket <= nextFree || nextFree <= i))
|
if (i < bucket && (bucket <= nextFree || nextFree <= i) ||
|
||||||
|| (bucket <= nextFree && nextFree <= i)) {
|
bucket <= nextFree && nextFree <= i) {
|
||||||
// Move the displaced entry "back" to the first available position.
|
// Move the displaced entry "back" to the first available position.
|
||||||
keys[nextFree] = keys[i];
|
keys[nextFree] = keys[i];
|
||||||
values[nextFree] = values[i];
|
values[nextFree] = values[i];
|
||||||
@ -412,7 +432,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
|
|||||||
V[] oldVals = values;
|
V[] oldVals = values;
|
||||||
|
|
||||||
keys = new int[newCapacity];
|
keys = new int[newCapacity];
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({ "unchecked", "SuspiciousArrayCast" })
|
||||||
V[] temp = (V[]) new Object[newCapacity];
|
V[] temp = (V[]) new Object[newCapacity];
|
||||||
values = temp;
|
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
|
// Inlined put(), but much simpler: we don't need to worry about
|
||||||
// duplicated keys, growing/rehashing, or failing to insert.
|
// duplicated keys, growing/rehashing, or failing to insert.
|
||||||
int oldKey = oldKeys[i];
|
int oldKey = oldKeys[i];
|
||||||
int startIndex = hashIndex(oldKey);
|
int index = hashIndex(oldKey);
|
||||||
int index = startIndex;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (values[index] == null) {
|
if (values[index] == null) {
|
||||||
|
@ -114,5 +114,10 @@ public interface IntObjectMap<V> {
|
|||||||
/**
|
/**
|
||||||
* Gets the values contained in this map.
|
* 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();
|
Collection<V> values();
|
||||||
}
|
}
|
||||||
|
@ -277,19 +277,28 @@ public class IntObjectHashMapTest {
|
|||||||
map.put(4, new Value("v4"));
|
map.put(4, new Value("v4"));
|
||||||
map.remove(4);
|
map.remove(4);
|
||||||
|
|
||||||
Collection<Value> values = map.values();
|
// Ensure values() return all values.
|
||||||
assertEquals(3, values.size());
|
|
||||||
|
|
||||||
Set<Value> expected = new HashSet<Value>();
|
Set<Value> expected = new HashSet<Value>();
|
||||||
|
Set<Value> actual = new HashSet<Value>();
|
||||||
|
|
||||||
expected.add(v1);
|
expected.add(v1);
|
||||||
expected.add(v2);
|
expected.add(v2);
|
||||||
expected.add(v3);
|
expected.add(v3);
|
||||||
|
|
||||||
Set<Value> found = new HashSet<Value>();
|
Value[] valueArray = map.values(Value.class);
|
||||||
for (Value value : values) {
|
assertEquals(3, valueArray.length);
|
||||||
assertTrue(found.add(value));
|
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
|
@Test
|
||||||
@ -315,14 +324,14 @@ public class IntObjectHashMapTest {
|
|||||||
map2.put(key, key);
|
map2.put(key, key);
|
||||||
}
|
}
|
||||||
assertEquals(map1.hashCode(), map2.hashCode());
|
assertEquals(map1.hashCode(), map2.hashCode());
|
||||||
assertTrue(map1.equals(map2));
|
assertEquals(map1, map2);
|
||||||
// Remove one "middle" element, maps should now be non-equals.
|
// Remove one "middle" element, maps should now be non-equals.
|
||||||
int[] keys = map1.keys();
|
int[] keys = map1.keys();
|
||||||
map2.remove(keys[50]);
|
map2.remove(keys[50]);
|
||||||
assertFalse(map1.equals(map2));
|
assertFalse(map1.equals(map2));
|
||||||
// Put it back; will likely be in a different position, but maps will be equal again.
|
// Put it back; will likely be in a different position, but maps will be equal again.
|
||||||
map2.put(keys[50], map1.keys()[50]);
|
map2.put(keys[50], map1.keys()[50]);
|
||||||
assertTrue(map1.equals(map2));
|
assertEquals(map1, map2);
|
||||||
assertEquals(map1.hashCode(), map2.hashCode());
|
assertEquals(map1.hashCode(), map2.hashCode());
|
||||||
// Make map2 have one extra element, will be non-equal.
|
// Make map2 have one extra element, will be non-equal.
|
||||||
map2.put(1000, 1000);
|
map2.put(1000, 1000);
|
||||||
@ -336,7 +345,7 @@ public class IntObjectHashMapTest {
|
|||||||
map2.put(key, key);
|
map2.put(key, key);
|
||||||
}
|
}
|
||||||
assertEquals(map1.hashCode(), map2.hashCode());
|
assertEquals(map1.hashCode(), map2.hashCode());
|
||||||
assertTrue(map1.equals(map2));
|
assertEquals(map1, map2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user