MATH-786
Allow caching of the hash code computation. User is expected to not change the pair contents if he requested the hash code to be cached. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1336458 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ec4edd9a20
commit
cb164171e6
|
@ -18,7 +18,10 @@ package org.apache.commons.math3.util;
|
|||
|
||||
/**
|
||||
* Generic pair.
|
||||
* Immutable class.
|
||||
* <br/>
|
||||
* Although the instances of this class are immutable, it is impossible
|
||||
* to ensure that the references passed to the constructor will not be
|
||||
* modified by the caller.
|
||||
*
|
||||
* @param <K> Key type.
|
||||
* @param <V> Value type.
|
||||
|
@ -31,6 +34,28 @@ public class Pair<K, V> {
|
|||
private final K key;
|
||||
/** Value. */
|
||||
private final V value;
|
||||
/** Whether the pair contents can be assumed to be immutable. */
|
||||
private final boolean isImmutable;
|
||||
/** Cached has code. */
|
||||
private final int cachedHashCode;
|
||||
|
||||
/**
|
||||
* Create an entry representing a mapping from the specified key to the
|
||||
* specified value.
|
||||
* If the pair can be assumed to be immutable, the hash code will be
|
||||
* cached.
|
||||
*
|
||||
* @param k Key.
|
||||
* @param v Value.
|
||||
* @param assumeImmutable Whether the pair contents can be assumed to
|
||||
* be immutable.
|
||||
*/
|
||||
public Pair(K k, V v, boolean assumeImmutable) {
|
||||
key = k;
|
||||
value = v;
|
||||
isImmutable = assumeImmutable;
|
||||
cachedHashCode = computeHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an entry representing a mapping from the specified key to the
|
||||
|
@ -40,8 +65,20 @@ public class Pair<K, V> {
|
|||
* @param v Value.
|
||||
*/
|
||||
public Pair(K k, V v) {
|
||||
key = k;
|
||||
value = v;
|
||||
this(k, v, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an entry representing the same mapping as the specified entry.
|
||||
* If the pair can be assumed to be immutable, the hash code will be
|
||||
* cached.
|
||||
*
|
||||
* @param entry Entry to copy.
|
||||
* @param assumeImmutable Whether the pair contents can be assumed to
|
||||
* be immutable.
|
||||
*/
|
||||
public Pair(Pair<? extends K, ? extends V> entry, boolean assumeImmutable) {
|
||||
this(entry.getKey(), entry.getValue(), assumeImmutable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,8 +87,7 @@ public class Pair<K, V> {
|
|||
* @param entry Entry to copy.
|
||||
*/
|
||||
public Pair(Pair<? extends K, ? extends V> entry) {
|
||||
key = entry.getKey();
|
||||
value = entry.getValue();
|
||||
this(entry, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,7 +140,20 @@ public class Pair<K, V> {
|
|||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
return isImmutable ? cachedHashCode : computeHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a hash code.
|
||||
*
|
||||
* @return the hash code value.
|
||||
*/
|
||||
private final int computeHashCode() {
|
||||
int result = key == null ? 0 : key.hashCode();
|
||||
|
||||
final int h = value == null ? 0 : value.hashCode();
|
||||
result = 37 * result + h ^ (h >>> 16);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,4 +46,69 @@ public class PairTest {
|
|||
Pair<Integer, Float> p3 = new Pair<Integer, Float>(new Integer(1), new Float(2));
|
||||
Assert.assertFalse(p1.equals(p3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
final MyInteger m1 = new MyInteger(1);
|
||||
final MyInteger m2 = new MyInteger(1);
|
||||
|
||||
final Pair<MyInteger, MyInteger> p1 = new Pair<MyInteger, MyInteger>(m1, m1);
|
||||
final Pair<MyInteger, MyInteger> p2 = new Pair<MyInteger, MyInteger>(m2, m2);
|
||||
// Same contents, same hash code.
|
||||
Assert.assertTrue(p1.hashCode() == p2.hashCode());
|
||||
|
||||
// Different contents, different hash codes.
|
||||
m2.set(2);
|
||||
Assert.assertFalse(p1.hashCode() == p2.hashCode());
|
||||
|
||||
// Test cache.
|
||||
|
||||
final MyInteger m3 = new MyInteger(1);
|
||||
final Pair<MyInteger, MyInteger> p3 = new Pair<MyInteger, MyInteger>(m3, m3, true);
|
||||
final int hC3 = p3.hashCode();
|
||||
// Contents change will not affect the hash code because it is cached.
|
||||
m3.set(3);
|
||||
Assert.assertTrue(hC3 == p3.hashCode());
|
||||
|
||||
final Pair<MyInteger, MyInteger> p4 = new Pair<MyInteger, MyInteger>(p3, false);
|
||||
// p3 and p4 do not have the same hash code because p4 was contructed after m3
|
||||
// was changed.
|
||||
Assert.assertFalse(p4.hashCode() == p3.hashCode());
|
||||
final int hC4 = p4.hashCode();
|
||||
// Contents change will affect the hash code because it is not cached.
|
||||
m3.set(4);
|
||||
Assert.assertFalse(hC4 == p4.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* A mutable integer.
|
||||
*/
|
||||
private static class MyInteger {
|
||||
private int i;
|
||||
|
||||
public MyInteger(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
public void set(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(o instanceof MyInteger)) {
|
||||
return false;
|
||||
} else {
|
||||
return i == ((MyInteger) o).i;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue