From 37fa5d6fa470c2a9879caf64b47b8e535753ebdb Mon Sep 17 00:00:00 2001 From: Stephen Colebourne Date: Mon, 11 Apr 2011 17:48:14 +0000 Subject: [PATCH] Enhance pair classes; Shorten toString form; Javadoc git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1091146 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/commons/lang3/ImmutablePair.java | 63 +++++---- .../org/apache/commons/lang3/MutablePair.java | 85 ++++++------ .../java/org/apache/commons/lang3/Pair.java | 121 +++++++++++++----- .../commons/lang3/ImmutablePairTest.java | 8 +- .../apache/commons/lang3/MutablePairTest.java | 8 +- .../org/apache/commons/lang3/PairTest.java | 37 +++++- 6 files changed, 215 insertions(+), 107 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/ImmutablePair.java b/src/main/java/org/apache/commons/lang3/ImmutablePair.java index 8d1ee1d07..0c7a8480a 100644 --- a/src/main/java/org/apache/commons/lang3/ImmutablePair.java +++ b/src/main/java/org/apache/commons/lang3/ImmutablePair.java @@ -17,16 +17,23 @@ package org.apache.commons.lang3; /** - * Immutable concrete manifestation of the {@link Pair} type. - * + *

An immutable pair consisting of two {@code Object} elements.

+ * + *

Although the implementation is immutable, there is no restriction on the objects + * that may be stored. If mutable objects are stored in the pair, then the pair + * itself effectively becomes mutable. The class is also not {@code final}, so a subclass + * could add undesirable behaviour.

+ * *

#ThreadSafe# if the objects are threadsafe

+ * + * @param the first element type + * @param the second element type + * * @since Lang 3.0 * @version $Id$ - * - * @param left generic type - * @param right generic type */ public class ImmutablePair extends Pair { + /** Serialization version */ private static final long serialVersionUID = 4954918890077093841L; @@ -36,10 +43,26 @@ public class ImmutablePair extends Pair { public final R right; /** - * Create a new ImmutablePair instance. + *

Obtains an immutable pair of from two objects inferring the generic types.

+ * + *

This factory allows the pair to be created using inference to + * obtain the generic types.

+ * + * @param the left element type + * @param the right element type + * @param left the left element, may be null + * @param right the right element, may be null + * @return a pair formed from the two parameters, not null + */ + public static ImmutablePair of(L left, R right) { + return new ImmutablePair(left, right); + } + + /** + * Create a new pair instance. * - * @param left the left value - * @param right the right value + * @param left the left value, may be null + * @param right the right value, may be null */ public ImmutablePair(L left, R right) { super(); @@ -47,6 +70,7 @@ public class ImmutablePair extends Pair { this.right = right; } + //----------------------------------------------------------------------- /** * {@inheritDoc} */ @@ -64,29 +88,16 @@ public class ImmutablePair extends Pair { } /** - * {@link java.util.Map.Entry#setValue(Object)} implementation. Because this - * class is immutable the {@code setValue()} operation is not supported. - * Therefore always an exception is thrown. + *

Throws {@code UnsupportedOperationException}.

+ * + *

This pair is immutable, so this operation is not supported.

* - * @param value the value to set - * @return the old right value + * @param value the value to set + * @return never * @throws UnsupportedOperationException as this operation is not supported */ public R setValue(R value) { throw new UnsupportedOperationException(); } - /** - * Static fluent creation method for an {@link ImmutablePair}: - * ImmutablePair.of(left, right) - * - * @param the left generic type - * @param the right generic type - * @param left the let value - * @param right the right value - * @return ImmutablePair(left, right) - */ - public static ImmutablePair of(L left, R right) { - return new ImmutablePair(left, right); - } } diff --git a/src/main/java/org/apache/commons/lang3/MutablePair.java b/src/main/java/org/apache/commons/lang3/MutablePair.java index d0f54a90d..ae5c34185 100644 --- a/src/main/java/org/apache/commons/lang3/MutablePair.java +++ b/src/main/java/org/apache/commons/lang3/MutablePair.java @@ -17,55 +17,77 @@ package org.apache.commons.lang3; /** - * Mutable concrete manifestation of the {@link Pair} type. + *

A mutable pair consisting of two {@code Object} elements.

+ * + *

Not #ThreadSafe#

+ * + * @param the first element type + * @param the second element type * - *

#ThreadSafe# if the objects are threadsafe

* @since Lang 3.0 * @version $Id$ - * - * @param left generic type - * @param right generic type */ public class MutablePair extends Pair { + /** Serialization version */ private static final long serialVersionUID = 4954918890077093841L; - private L leftElement; - private R rightElement; + /** Left object */ + public L left; + /** Right object */ + public R right; /** - * Create a new MutablePair instance. + *

Obtains an immutable pair of from two objects inferring the generic types.

+ * + *

This factory allows the pair to be created using inference to + * obtain the generic types.

+ * + * @param the left element type + * @param the right element type + * @param left the left element, may be null + * @param right the right element, may be null + * @return a pair formed from the two parameters, not null + */ + public static MutablePair of(L left, R right) { + return new MutablePair(left, right); + } + + /** + * Create a new pair instance of two nulls. */ public MutablePair() { super(); } /** - * Create a new MutablePair instance. + * Create a new pair instance. * - * @param leftElement the left value - * @param rightElement the right value + * @param left the left value, may be null + * @param right the right value, may be null */ - public MutablePair(L leftElement, R rightElement) { + public MutablePair(L left, R right) { super(); - this.leftElement = leftElement; - this.rightElement = rightElement; + this.left = left; + this.right = right; } + //----------------------------------------------------------------------- /** * {@inheritDoc} */ @Override public L getLeftElement() { - return leftElement; + return left; } /** * Set the left element of the pair. - * @param leftElement the value of the left element + * + * @param left the new value of the left element, may be null */ - public void setLeftElement(L leftElement) { - this.leftElement = leftElement; + public void setLeftElement(L left) { + this.left = left; } /** @@ -73,20 +95,23 @@ public class MutablePair extends Pair { */ @Override public R getRightElement() { - return rightElement; + return right; } /** * Set the right element of the pair. - * @param rightElement the value of the right element + * + * @param right the value of the right element */ - public void setRightElement(R rightElement) { - this.rightElement = rightElement; + public void setRightElement(R right) { + this.right = right; } /** - * Implement {@link java.util.Map.Entry#setValue(Object)}. - * @param value value (rightElement) to set + * Sets the {@code Map.Entry} value. + * This sets the right element of the pair. + * + * @param value the right value to set, not null * @return the old value for the right element */ public R setValue(R value) { @@ -95,16 +120,4 @@ public class MutablePair extends Pair { return result; } - /** - * Static fluent creation method for a {@link MutablePair}: - * MutablePair.of(left, right) - * @param the left generic type - * @param the right generic type - * @param left the left value - * @param right the right value - * @return MutablePair(left, right) - */ - public static MutablePair of(L left, R right) { - return new MutablePair(left, right); - } } diff --git a/src/main/java/org/apache/commons/lang3/Pair.java b/src/main/java/org/apache/commons/lang3/Pair.java index d83654ac5..9398013ac 100644 --- a/src/main/java/org/apache/commons/lang3/Pair.java +++ b/src/main/java/org/apache/commons/lang3/Pair.java @@ -19,79 +19,142 @@ package org.apache.commons.lang3; import java.io.Serializable; import java.util.Map; -import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.CompareToBuilder; /** - * Abstract Pair (or 2-element Tuple). + *

A pair consisting of two elements.

+ * + *

This class is an abstract implementation defining the basic API. + * It refers to the elements as 'left' and 'right'. It also implements the + * {@code Map.Entry} interface where the key is 'left' and the value is 'right'.

+ * + *

Subclass implementations may be mutable or immutable. + * However, there is no restriction on the type of the stored objects that may be stored. + * If mutable objects are stored in the pair, then the pair itself effectively becomes mutable.

+ * + * @param the first element type + * @param the second element type * * @since Lang 3.0 * @version $Id$ */ -public abstract class Pair implements Serializable, Map.Entry { +public abstract class Pair implements Map.Entry, Comparable>, Serializable { + /** Serialization version */ private static final long serialVersionUID = 4954918890077093841L; /** - * Get the "left" element of the pair. - * @return L + *

Obtains an immutable pair of from two objects inferring the generic types.

+ * + *

This factory allows the pair to be created using inference to + * obtain the generic types.

+ * + * @param the left element type + * @param the right element type + * @param left the left element, may be null + * @param right the right element, may be null + * @return a pair formed from the two parameters, not null + */ + public static Pair of(L left, R right) { + return new ImmutablePair(left, right); + } + + //----------------------------------------------------------------------- + /** + *

Gets the left element from this pair.

+ * + *

When treated as a key-value pair, this is the key.

+ * + * @return the left element, may be null */ public abstract L getLeftElement(); /** - * Get the "right" element of the pair. - * @return R + *

Gets the right element from this pair.

+ * + *

When treated as a key-value pair, this is the value.

+ * + * @return the right element, may be null */ public abstract R getRightElement(); /** - * Return {@link #getLeftElement()} as a {@link java.util.Map.Entry}'s key. - * @return L + *

Gets the key from this pair.

+ * + *

This method implements the {@code Map.Entry} interface returning the + * left element as the key.

+ * + * @return the left element as the key, may be null */ public final L getKey() { return getLeftElement(); } /** - * Return {@link #getRightElement()} as a {@link java.util.Map.Entry}'s value. - * @return R + *

Gets the value from this pair.

+ * + *

This method implements the {@code Map.Entry} interface returning the + * right element as the value.

+ * + * @return the right element as the value, may be null */ public R getValue() { return getRightElement(); } + //----------------------------------------------------------------------- /** - * {@inheritDoc} + *

Compares the pair based on the first element followed by the second element. + * The types must be {@code Comparable}.

+ * + * @param other the other pair, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(Pair other) { + return new CompareToBuilder().append(getLeftElement(), other.getLeftElement()) + .append(getRightElement(), other.getRightElement()).toComparison(); + } + + /** + *

Compares this pair to another based on the two elements.

+ * + * @param obj the object to compare to, null returns false + * @return true if the elements of the pair are equal */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } - if (obj instanceof Pair == false) { - return false; + if (obj instanceof Map.Entry) { + Map.Entry other = (Map.Entry) obj; + return ObjectUtils.equals(getKey(), other.getKey()) + && ObjectUtils.equals(getValue(), other.getValue()); } - Pair other = (Pair) obj; - return ObjectUtils.equals(getLeftElement(), other.getLeftElement()) - && ObjectUtils.equals(getRightElement(), other.getRightElement()); + return false; } /** - * {@inheritDoc} + *

Returns a suitable hash code. + * The hash code follows the definition in {@code Map.Entry}.

+ * + * @return the hash code */ @Override public int hashCode() { - // TODO should the hashCodeBuilder be seeded per concrete type? - return new HashCodeBuilder().append(getLeftElement()).append(getRightElement()) - .toHashCode(); + // see Map.Entry API specification + return (getKey() == null ? 0 : getKey().hashCode()) ^ + (getValue() == null ? 0 : getValue().hashCode()); } /** - * Returns a String representation of the Pair in the form: (L,R) + *

Returns a String representation of the Pair in the form: (L,R).

+ * * @return a string for this object */ @Override public String toString() { - StringBuilder builder = new StringBuilder(this.getClass().getSimpleName()); + StringBuilder builder = new StringBuilder(); builder.append("("); builder.append(getLeftElement()); builder.append(","); @@ -100,16 +163,4 @@ public abstract class Pair implements Serializable, Map.Entry { return builder.toString(); } - /** - * Static fluent creation method for a {@link Pair}: - * Pair.of(left, right) - * @param the left generic type - * @param the right generic type - * @param left the left value - * @param right the right value - * @return ImmutablePair(left, right) - */ - public static Pair of(L left, R right) { - return new ImmutablePair(left, right); - } } diff --git a/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java b/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java index 1574849ad..e70b0c056 100644 --- a/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java +++ b/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java @@ -80,10 +80,10 @@ public class ImmutablePairTest { @Test public void testToString() throws Exception { - assertEquals("ImmutablePair(null,null)", ImmutablePair.of(null, null).toString()); - assertEquals("ImmutablePair(null,two)", ImmutablePair.of(null, "two").toString()); - assertEquals("ImmutablePair(one,null)", ImmutablePair.of("one", null).toString()); - assertEquals("ImmutablePair(one,two)", ImmutablePair.of("one", "two").toString()); + assertEquals("(null,null)", ImmutablePair.of(null, null).toString()); + assertEquals("(null,two)", ImmutablePair.of(null, "two").toString()); + assertEquals("(one,null)", ImmutablePair.of("one", null).toString()); + assertEquals("(one,two)", ImmutablePair.of("one", "two").toString()); } @Test diff --git a/src/test/java/org/apache/commons/lang3/MutablePairTest.java b/src/test/java/org/apache/commons/lang3/MutablePairTest.java index 85e069e6f..a09d206ba 100644 --- a/src/test/java/org/apache/commons/lang3/MutablePairTest.java +++ b/src/test/java/org/apache/commons/lang3/MutablePairTest.java @@ -88,10 +88,10 @@ public class MutablePairTest { @Test public void testToString() throws Exception { - assertEquals("MutablePair(null,null)", MutablePair.of(null, null).toString()); - assertEquals("MutablePair(null,two)", MutablePair.of(null, "two").toString()); - assertEquals("MutablePair(one,null)", MutablePair.of("one", null).toString()); - assertEquals("MutablePair(one,two)", MutablePair.of("one", "two").toString()); + assertEquals("(null,null)", MutablePair.of(null, null).toString()); + assertEquals("(null,two)", MutablePair.of(null, "two").toString()); + assertEquals("(one,null)", MutablePair.of("one", null).toString()); + assertEquals("(one,two)", MutablePair.of("one", "two").toString()); } @Test diff --git a/src/test/java/org/apache/commons/lang3/PairTest.java b/src/test/java/org/apache/commons/lang3/PairTest.java index 9358192ed..0fe496cc7 100644 --- a/src/test/java/org/apache/commons/lang3/PairTest.java +++ b/src/test/java/org/apache/commons/lang3/PairTest.java @@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map.Entry; import org.junit.Test; @@ -44,7 +46,7 @@ public class PairTest { } @Test - public void testCompatibility() throws Exception { + public void testCompatibilityBetweenPairs() throws Exception { Pair pair = ImmutablePair.of(0, "foo"); Pair pair2 = MutablePair.of(0, "foo"); assertEquals(pair, pair2); @@ -58,9 +60,40 @@ public class PairTest { assertFalse(pair.hashCode() == pair2.hashCode()); } + @Test + public void testMapEntry() throws Exception { + Pair pair = ImmutablePair.of(0, "foo"); + HashMap map = new HashMap(); + map.put(0, "foo"); + Entry entry = map.entrySet().iterator().next(); + assertEquals(pair, entry); + assertEquals(pair.hashCode(), entry.hashCode()); + } + + @Test + public void testComparable1() throws Exception { + Pair pair1 = Pair.of("A", "D"); + Pair pair2 = Pair.of("B", "C"); + assertEquals(true, pair1.compareTo(pair1) == 0); + assertEquals(true, pair1.compareTo(pair2) < 0); + assertEquals(true, pair2.compareTo(pair2) == 0); + assertEquals(true, pair2.compareTo(pair1) > 0); + } + + @Test + public void testComparable2() throws Exception { + Pair pair1 = Pair.of("A", "C"); + Pair pair2 = Pair.of("A", "D"); + assertEquals(true, pair1.compareTo(pair1) == 0); + assertEquals(true, pair1.compareTo(pair2) < 0); + assertEquals(true, pair2.compareTo(pair2) == 0); + assertEquals(true, pair2.compareTo(pair1) > 0); + } + @Test public void testToString() throws Exception { Pair pair = Pair.of("Key", "Value"); - assertEquals("ImmutablePair(Key,Value)", pair.toString()); + assertEquals("(Key,Value)", pair.toString()); } + }