MultiKey enhancements to add getKey(index) and size()
Make protected constructor public Add lots of javadoc examples and warnings git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131601 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5ecf769b32
commit
320589dca3
|
@ -34,7 +34,18 @@ NEW CLASSES
|
|||
|
||||
<hr />
|
||||
|
||||
<center><h3>BUG FIXES</h3></center>
|
||||
<p>
|
||||
[27159] AbstractHashedMap subclasses failed to clone() correctly
|
||||
</p>
|
||||
|
||||
<center><h3>ENHANCEMENTS</h3></center>
|
||||
<p>
|
||||
MultiKey - Add getKey(index) and size() methods and make constructor public
|
||||
</p>
|
||||
|
||||
<center><h3>CHANGES</h3></center>
|
||||
<p>
|
||||
[26470] Javadoc - Add javadoc about requiring Comparable entries
|
||||
[27159] Bug - AbstractHashedMap subclasses failed to clone() correctly
|
||||
[26470] TreeBidiMap - Add javadoc about requiring Comparable entries
|
||||
MultiKey - Add extra explanatations, examples and warnings
|
||||
</p>
|
||||
|
|
|
@ -25,14 +25,27 @@ import java.util.Arrays;
|
|||
* maps of maps. An example might be the need to lookup a filename by
|
||||
* key and locale. The typical solution might be nested maps. This class
|
||||
* can be used instead by creating an instance passing in the key and locale.
|
||||
* <p>
|
||||
* Example usage:
|
||||
* <pre>
|
||||
* // populate map with data mapping key+locale to localizedText
|
||||
* Map map = new HashMap();
|
||||
* MultiKey multiKey = new MultiKey(key, locale);
|
||||
* map.put(multiKey, localizedText);
|
||||
*
|
||||
* // later retireve the localized text
|
||||
* MultiKey multiKey = new MultiKey(key, locale);
|
||||
* String localizedText = (String) map.get(multiKey);
|
||||
* </pre>
|
||||
*
|
||||
* @since Commons Collections 3.0
|
||||
* @version $Revision: 1.4 $ $Date: 2004/02/18 01:00:08 $
|
||||
* @version $Revision: 1.5 $ $Date: 2004/03/13 12:43:43 $
|
||||
*
|
||||
* @author Howard Lewis Ship
|
||||
* @author Stephen Colebourne
|
||||
*/
|
||||
public class MultiKey implements Serializable {
|
||||
// This class could implement List, but that would confuse it's purpose
|
||||
|
||||
/** Serialisation version */
|
||||
private static final long serialVersionUID = 4465448607415788805L;
|
||||
|
@ -44,6 +57,9 @@ public class MultiKey implements Serializable {
|
|||
|
||||
/**
|
||||
* Constructor taking two keys.
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed after adding to the MultiKey.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param key2 the second key
|
||||
|
@ -54,6 +70,9 @@ public class MultiKey implements Serializable {
|
|||
|
||||
/**
|
||||
* Constructor taking three keys.
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed after adding to the MultiKey.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param key2 the second key
|
||||
|
@ -65,6 +84,9 @@ public class MultiKey implements Serializable {
|
|||
|
||||
/**
|
||||
* Constructor taking four keys.
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed after adding to the MultiKey.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param key2 the second key
|
||||
|
@ -77,6 +99,9 @@ public class MultiKey implements Serializable {
|
|||
|
||||
/**
|
||||
* Constructor taking five keys.
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed after adding to the MultiKey.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param key2 the second key
|
||||
|
@ -89,9 +114,14 @@ public class MultiKey implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructor taking an array of keys.
|
||||
* Constructor taking an array of keys which is cloned.
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed after adding to the MultiKey.
|
||||
* <p>
|
||||
* This is equivalent to <code>new MultiKey(keys, true)</code>.
|
||||
*
|
||||
* @param keys the array of keys
|
||||
* @param keys the array of keys, not null
|
||||
* @throws IllegalArgumentException if the key array is null
|
||||
*/
|
||||
public MultiKey(Object[] keys) {
|
||||
|
@ -99,20 +129,35 @@ public class MultiKey implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructor taking an array of keys.
|
||||
* Constructor taking an array of keys, optionally choosing whether to clone.
|
||||
* <p>
|
||||
* If the array is not copied, then it must not be modified.
|
||||
* <b>If the array is not cloned, then it must not be modified.</b>
|
||||
* <p>
|
||||
* This method is public for performance reasons only, to avoid a clone.
|
||||
* The hashcode is calculated once here in this method.
|
||||
* Therefore, changing the array passed in would not change the hashcode but
|
||||
* would change the equals method, which is a bug.
|
||||
* <p>
|
||||
* This is the only fully safe usage of this constructor, as the object array
|
||||
* is never made available in a variable:
|
||||
* <pre>
|
||||
* new MultiKey(new Object[] {...}, false);
|
||||
* </pre>
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed after adding to the MultiKey.
|
||||
*
|
||||
* @param keys the array of keys
|
||||
* @param makeCopy true to copy the array, false to assign it
|
||||
* @param keys the array of keys, not null
|
||||
* @param makeClone true to clone the array, false to assign it
|
||||
* @throws IllegalArgumentException if the key array is null
|
||||
* @since Commons Collections 3.1
|
||||
*/
|
||||
protected MultiKey(Object[] keys, boolean makeCopy) {
|
||||
public MultiKey(Object[] keys, boolean makeClone) {
|
||||
super();
|
||||
if (keys == null) {
|
||||
throw new IllegalArgumentException("The array of keys must not be null");
|
||||
}
|
||||
if (makeCopy) {
|
||||
if (makeClone) {
|
||||
this.keys = (Object[]) keys.clone();
|
||||
} else {
|
||||
this.keys = keys;
|
||||
|
@ -121,18 +166,18 @@ public class MultiKey implements Serializable {
|
|||
int total = 0;
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
if (keys[i] != null) {
|
||||
if (i == 0) {
|
||||
total = keys[i].hashCode();
|
||||
} else {
|
||||
total ^= keys[i].hashCode();
|
||||
}
|
||||
total ^= keys[i].hashCode();
|
||||
}
|
||||
}
|
||||
hashCode = total;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Gets a copy of the individual keys.
|
||||
* Gets a clone of the array of keys.
|
||||
* <p>
|
||||
* The keys should be immutable
|
||||
* If they are not then they must not be changed.
|
||||
*
|
||||
* @return the individual keys
|
||||
*/
|
||||
|
@ -140,6 +185,32 @@ public class MultiKey implements Serializable {
|
|||
return (Object[]) keys.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key at the specified index.
|
||||
* <p>
|
||||
* The key should be immutable.
|
||||
* If it is not then it must not be changed.
|
||||
*
|
||||
* @param index the index to retrieve
|
||||
* @return the key at the index
|
||||
* @throws IndexOutOfBoundsException if the index is invalid
|
||||
* @since Commons Collections 3.1
|
||||
*/
|
||||
public Object getKey(int index) {
|
||||
return keys[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the list of keys.
|
||||
*
|
||||
* @return the size of the list of keys
|
||||
* @since Commons Collections 3.1
|
||||
*/
|
||||
public int size() {
|
||||
return keys.length;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Compares this object to another.
|
||||
* <p>
|
||||
|
|
|
@ -25,7 +25,7 @@ import junit.framework.TestSuite;
|
|||
/**
|
||||
* Unit tests for {@link org.apache.commons.collections.MultiKey}.
|
||||
*
|
||||
* @version $Revision: 1.3 $ $Date: 2004/02/18 01:20:40 $
|
||||
* @version $Revision: 1.4 $ $Date: 2004/03/13 12:43:43 $
|
||||
*
|
||||
* @author Stephen Colebourne
|
||||
*/
|
||||
|
@ -58,8 +58,8 @@ public class TestMultiKey extends TestCase {
|
|||
super.tearDown();
|
||||
}
|
||||
|
||||
|
||||
public void testConstructorsAndGet() throws Exception {
|
||||
//-----------------------------------------------------------------------
|
||||
public void testConstructors() throws Exception {
|
||||
MultiKey mk = null;
|
||||
mk = new MultiKey(ONE, TWO);
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {ONE, TWO}, mk.getKeys()));
|
||||
|
@ -75,13 +75,109 @@ public class TestMultiKey extends TestCase {
|
|||
|
||||
mk = new MultiKey(new Object[] {THREE, FOUR, ONE, TWO}, false);
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys()));
|
||||
|
||||
// don't do this!
|
||||
}
|
||||
|
||||
public void testConstructorsByArray() throws Exception {
|
||||
MultiKey mk = null;
|
||||
Object[] keys = new Object[] {THREE, FOUR, ONE, TWO};
|
||||
mk = new MultiKey(keys);
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys()));
|
||||
keys[3] = FIVE; // no effect
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys()));
|
||||
|
||||
keys = new Object[] {};
|
||||
mk = new MultiKey(keys);
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {}, mk.getKeys()));
|
||||
|
||||
keys = new Object[] {THREE, FOUR, ONE, TWO};
|
||||
mk = new MultiKey(keys, true);
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys()));
|
||||
keys[3] = FIVE; // no effect
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys()));
|
||||
|
||||
keys = new Object[] {THREE, FOUR, ONE, TWO};
|
||||
mk = new MultiKey(keys, false);
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, TWO}, mk.getKeys()));
|
||||
// change key - don't do this!
|
||||
// the hashcode of the MultiKey is now broken
|
||||
keys[3] = FIVE;
|
||||
Assert.assertTrue(Arrays.equals(new Object[] {THREE, FOUR, ONE, FIVE}, mk.getKeys()));
|
||||
}
|
||||
|
||||
public void testConstructorsByArrayNull() throws Exception {
|
||||
Object[] keys = null;
|
||||
try {
|
||||
new MultiKey(keys);
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {}
|
||||
try {
|
||||
new MultiKey(keys, true);
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {}
|
||||
try {
|
||||
new MultiKey(keys, false);
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {}
|
||||
}
|
||||
|
||||
public void testSize() {
|
||||
Assert.assertEquals(2, new MultiKey(ONE, TWO).size());
|
||||
Assert.assertEquals(2, new MultiKey(null, null).size());
|
||||
Assert.assertEquals(3, new MultiKey(ONE, TWO, THREE).size());
|
||||
Assert.assertEquals(3, new MultiKey(null, null, null).size());
|
||||
Assert.assertEquals(4, new MultiKey(ONE, TWO, THREE, FOUR).size());
|
||||
Assert.assertEquals(4, new MultiKey(null, null, null, null).size());
|
||||
Assert.assertEquals(5, new MultiKey(ONE, TWO, THREE, FOUR, FIVE).size());
|
||||
Assert.assertEquals(5, new MultiKey(null, null, null, null, null).size());
|
||||
|
||||
Assert.assertEquals(0, new MultiKey(new Object[] {}).size());
|
||||
Assert.assertEquals(1, new MultiKey(new Object[] {ONE}).size());
|
||||
Assert.assertEquals(2, new MultiKey(new Object[] {ONE, TWO}).size());
|
||||
Assert.assertEquals(7, new MultiKey(new Object[] {ONE, TWO, ONE, TWO, ONE, TWO, ONE}).size());
|
||||
}
|
||||
|
||||
public void testGetIndexed() {
|
||||
MultiKey mk = new MultiKey(ONE, TWO);
|
||||
Assert.assertSame(ONE, mk.getKey(0));
|
||||
Assert.assertSame(TWO, mk.getKey(1));
|
||||
try {
|
||||
mk.getKey(-1);
|
||||
fail();
|
||||
} catch (IndexOutOfBoundsException ex) {}
|
||||
try {
|
||||
mk.getKey(2);
|
||||
fail();
|
||||
} catch (IndexOutOfBoundsException ex) {}
|
||||
}
|
||||
|
||||
public void testGetKeysSimpleConstructor() {
|
||||
MultiKey mk = new MultiKey(ONE, TWO);
|
||||
Object[] array = mk.getKeys();
|
||||
Assert.assertSame(ONE, array[0]);
|
||||
Assert.assertSame(TWO, array[1]);
|
||||
Assert.assertEquals(2, array.length);
|
||||
}
|
||||
|
||||
public void testGetKeysArrayConstructorCloned() {
|
||||
Object[] keys = new Object[] {ONE, TWO};
|
||||
MultiKey mk = new MultiKey(keys, true);
|
||||
Object[] array = mk.getKeys();
|
||||
Assert.assertTrue(array != keys);
|
||||
Assert.assertTrue(Arrays.equals(array, keys));
|
||||
Assert.assertSame(ONE, array[0]);
|
||||
Assert.assertSame(TWO, array[1]);
|
||||
Assert.assertEquals(2, array.length);
|
||||
}
|
||||
|
||||
public void testGetKeysArrayConstructorNonCloned() {
|
||||
Object[] keys = new Object[] {ONE, TWO};
|
||||
MultiKey mk = new MultiKey(keys, false);
|
||||
Object[] array = mk.getKeys();
|
||||
Assert.assertTrue(array != keys); // still not equal
|
||||
Assert.assertTrue(Arrays.equals(array, keys));
|
||||
Assert.assertSame(ONE, array[0]);
|
||||
Assert.assertSame(TWO, array[1]);
|
||||
Assert.assertEquals(2, array.length);
|
||||
}
|
||||
|
||||
public void testHashCode() {
|
||||
|
@ -92,6 +188,9 @@ public class TestMultiKey extends TestCase {
|
|||
Assert.assertTrue(mk1.hashCode() == mk1.hashCode());
|
||||
Assert.assertTrue(mk1.hashCode() == mk2.hashCode());
|
||||
Assert.assertTrue(mk1.hashCode() != mk3.hashCode());
|
||||
|
||||
int total = (0 ^ ONE.hashCode()) ^ TWO.hashCode();
|
||||
Assert.assertEquals(total, mk1.hashCode());
|
||||
}
|
||||
|
||||
public void testEquals() {
|
||||
|
|
Loading…
Reference in New Issue