You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2012/05/10 02:14:09 UTC

svn commit: r1336458 - in /commons/proper/math/trunk/src: main/java/org/apache/commons/math3/util/Pair.java test/java/org/apache/commons/math3/util/PairTest.java

Author: erans
Date: Thu May 10 00:14:08 2012
New Revision: 1336458

URL: http://svn.apache.org/viewvc?rev=1336458&view=rev
Log:
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.

Modified:
    commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Pair.java
    commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PairTest.java

Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Pair.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Pair.java?rev=1336458&r1=1336457&r2=1336458&view=diff
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Pair.java (original)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/Pair.java Thu May 10 00:14:08 2012
@@ -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,17 +34,51 @@ 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) {
+    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
+     * specified value.
+     *
+     * @param k Key.
+     * @param v Value.
+     */
+    public Pair(K k, V 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;
     }
 }

Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PairTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PairTest.java?rev=1336458&r1=1336457&r2=1336458&view=diff
==============================================================================
--- commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PairTest.java (original)
+++ commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/PairTest.java Thu May 10 00:14:08 2012
@@ -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;
+        }
+    }
 }