Attached is source (& associated tests) for KeyValueRecord.java.
KeyValueRecord is an immutable {key, value, previous-value} triplet, a
simple but very useful data structure. It's particularly of use with
Map (and Map.Entry) for storing, tracking, and comparing entry state,
but can obviously be used for any key-value data. The constructors
include one to create a KeyValueRecord from a Map.Entry. There is also
a method to view the KeyValueRecord as a Map.Entry (#asMapEntry). See
the javadoc for more details.** I had a swift look through [collections] and [lang], and didn't see a simple KeyValuePair implementation... Have I missed this somewhere? If not, I have an implementation which I will post. Thanks, Neil
/* * $Header: x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/java/org/apache/commons/collections/KeyValueRecord.java,v 1.2 2003/09/20 22:00:32 otoolen Exp $ * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.collections; import java.util.Map; /** * An immutable {key, value, previous-value} triplet. This class is frequently used * in conjunction with <code>Map.Entry</code>. A constructor is provided to create * a <code>KeyValueRecord</code> from a <code>Map.Entry</code>, and the [EMAIL PROTECTED] #asMapEntry} * method can be used to view an object of this class as a <code>Map.Entry</code>. * Note that it is not possible for <code>KeyValueRecord</code> to implement the <code>Map.Entry</code> * interface as the <code>#equals</code> implementations are not compatible. * * * * @author Neil O'Toole */ public class KeyValueRecord { private static final Object NO_PREVIOUS_VALUE = new Object(); private int hash = -1; // lazily calculated private final Object key; private final Object previous; private final Object value; /** * Create a new <code>KeyValueRecord</code> with key and value from the supplied * <code>Map.Entry</code> and no previous value. */ public KeyValueRecord(final Map.Entry entry) { this(entry.getKey(), entry.getValue(), NO_PREVIOUS_VALUE); } /** * Create a new <code>KeyValueRecord</code> with the specified key and value and * no previous value. */ public KeyValueRecord(final Object key, final Object value) { this(key, value, NO_PREVIOUS_VALUE); } /** * Create a new <code>KeyValueRecord</code> with the specified key, value, and previous value. */ public KeyValueRecord(final Object key, final Object value, final Object previousValue) { this.key = key; this.value = value; this.previous = previousValue; } /** * Returns a [EMAIL PROTECTED] Map.Entry} view of the supplied <code>KeyValueRecord</code>. The * returned entry is unmodifiable (<code>#setValue</code> throws an [EMAIL PROTECTED] UnsupportedOperationException}), * as the backing <code>KeyValueRecord</code> is itself immutable. * The returned entry correctly implements the <code>#hashCode</code> and * <code>#equals</code> operations as per the <code>Map.Entry</code> contract. */ public Map.Entry asMapEntry() { return new Map.Entry() { private int hash = -1; public boolean equals(Object o) { if (o instanceof Map.Entry == false) { return false; } if (o == this) { return true; } Map.Entry e = (Map.Entry) o; return ( KeyValueRecord.this.getKey() == null ? e.getKey() == null : KeyValueRecord.this.getKey().equals(e.getKey())) && (KeyValueRecord.this.getValue() == null ? e.getValue() == null : KeyValueRecord.this.getValue().equals(e.getValue())); } public Object getKey() { return KeyValueRecord.this.getKey(); } public Object getValue() { return KeyValueRecord.this.getValue(); } public int hashCode() { if (this.hash == -1) { this.hash = (KeyValueRecord.this.getKey() == null ? 0 : KeyValueRecord.this.getKey().hashCode()) ^ (KeyValueRecord.this.getValue() == null ? 0 : KeyValueRecord.this.getValue().hashCode()); } return this.hash; } public Object setValue(Object value) { throw new UnsupportedOperationException("This Map.Entry is unmodifiable."); } public String toString() { return new StringBuffer() .append(KeyValueRecord.this.getKey()) .append('=') .append(KeyValueRecord.this.getValue()) .toString(); } }; } /** * Compares the specified object with this <code>KeyValueRecord</code> for equality. Returns * true if the given object is also a <code>KeyValueRecord</code> and * the records' key, value, and previous value are equal. * * @param o object to be compared for equality with this <code>KeyValueRecord</code>. * @return <code>true</code> if the specified object is equal to this * record. */ public boolean equals(final Object o) { if (!(o instanceof KeyValueRecord)) { return false; } if (this == o) { return true; } final KeyValueRecord kvr = (KeyValueRecord) o; return (this.key == null ? kvr.key == null : this.key.equals(kvr.key)) && (this.value == null ? kvr.value == null : this.value.equals(kvr.value)) && (this.previous == NO_PREVIOUS_VALUE ? (kvr.previous == NO_PREVIOUS_VALUE) : (this.previous == null ? kvr.previous == null : this.previous.equals(kvr.previous))); } /** * Returns the key associated with this record. */ public Object getKey() { return this.key; } /** * Returns the value previously associated with this record's key, if any. Note that * <code>null</code> will be returned * if the previous value is <code>null</code> <i>or</i> if there is no previous value. * Therefore [EMAIL PROTECTED] #hasPreviousValue()} should be used to test if there is a previous value. * * @return the previous value (which may be <code>null</code>) * associated with this record's key, or <code>null</code> if there is no * previous value. */ public Object getPreviousValue() { return (this.previous == NO_PREVIOUS_VALUE) ? null : this.previous; } /** * Returns the value associated with this record's key. * * @return the object (which may be <code>null</code>) * associated with this record's key */ public Object getValue() { return this.value; } public int hashCode() { if (this.hash == -1) { this.hash = (this.key == null ? 0 : this.key.hashCode()) ^ (this.value == null ? 0 : this.value.hashCode()) ^ (this.previous == null ? 0 : this.previous.hashCode()); } return this.hash; } /** * Returns true if this record's key * was associated with a value previous to being * associated with its current value. * * @return <code>true</code> if this key previously had a value associated * with it, <code>false</code> otherwise. * @see #getPreviousValue() */ public boolean hasPreviousValue() { return this.previous != NO_PREVIOUS_VALUE; } /** * Returns a string representation of this <code>KeyValueRecord</code>. The text * rendering is <code>key=value</code> or <code>key=value (previous)</code> * depending on whether this <code>KeyValueRecord</code> * has a previous value. * */ public String toString() { final StringBuffer sb = new StringBuffer().append(this.key).append('=').append(this.value); if (this.hasPreviousValue()) { sb.append(' ').append('(').append(this.getPreviousValue()).append(')'); } return sb.toString(); } }
/* * $Header: x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/commons/collections/TestKeyValueRecord.java,v 1.1 2003/09/20 22:00:46 otoolen Exp $ * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.collections; import java.util.HashMap; import java.util.Map; /* * $Header: x:/apps/cvsnt/cvs_repository/main/notifyingcollections/src/test/org/apache/commons/collections/TestKeyValueRecord.java,v 1.1 2003/09/20 22:00:46 otoolen Exp $ * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ import junit.framework.TestCase; import org.apache.commons.collections.KeyValueRecord; /** * * @author Neil O'Toole */ public class TestKeyValueRecord extends TestCase { private final String key = "key"; private final String value = "value"; private final String previous = "previous"; public TestKeyValueRecord(String testName) { super(testName); } public static void main(String[] args) { junit.textui.TestRunner.run(TestKeyValueRecord.class); } public void testKeyValueRecord() { // KVR with no previous value KeyValueRecord kvr = new KeyValueRecord(key, value); assertTrue( kvr.getKey() == key && kvr.getValue() == value && kvr.getPreviousValue() == null && kvr.hasPreviousValue() == false); assertTrue(kvr.equals(kvr)); assertTrue(kvr.toString().equals("key=value")); // KVR with a previous value KeyValueRecord kvr2 = new KeyValueRecord(key, value, previous); assertTrue( kvr2.getKey() == key && kvr2.getValue() == value && kvr2.getPreviousValue() == previous && kvr2.hasPreviousValue() == true); assertTrue(kvr2.equals(kvr2)); assertTrue(kvr2.toString().equals("key=value (previous)")); assertFalse(kvr.equals(kvr2)); assertFalse(kvr.hashCode() == kvr2.hashCode()); // test that a previous value of 'null' is treated differently to no previous value KeyValueRecord kvr3 = new KeyValueRecord(key, value, null); assertTrue(kvr3.equals(kvr3)); assertTrue(kvr3.toString().equals("key=value (null)")); assertFalse(kvr3.equals(kvr)); assertFalse(kvr3.equals(kvr2)); assertFalse(kvr3.hashCode() == kvr2.hashCode()); assertFalse(kvr3.hashCode() == kvr.hashCode()); // test the Map.Entry handling Map map = new HashMap(); map.put(key, value); Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); KeyValueRecord kvr4 = new KeyValueRecord(entry); assertTrue( kvr4.getKey() == key && kvr4.getValue() == value && kvr4.getPreviousValue() == null && kvr4.hasPreviousValue() == false); assertTrue(kvr4.equals(kvr4)); assertTrue(kvr4.equals(kvr)); assertTrue(kvr4.toString().equals("key=value")); assertFalse("Map.Entry and KeyValueRecord are never equal", kvr4.equals(entry)); Map.Entry entry2 = kvr4.asMapEntry(); assertTrue(entry.equals(entry2)); assertTrue(entry.hashCode() == entry2.hashCode()); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
