You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@commons.apache.org by Stephen Colebourne <sc...@btopenworld.com> on 2004/10/10 20:09:12 UTC

Re: [collections] "safe" maps, sets and lists

It sounds like you should look at

LazyMap/LazyList, for populating missing map/list entries
Predicated*, for validating input
Typed*, for validating against a type
The functor subpackage for standard factory/predicate implementations

Stephen

----- Original Message -----
From: "B. K. Oxley (binkley)" <bi...@alumni.rice.edu>
> I'm looking through the commons collection for "safer" version of the
standard
> map, set and list implementations and not finding quite what I want.  To
be
> more specific, I'd like maps that disallow keys, and maps, sets and lists
> which disallow null values.  Further, I'd like to enforce type safety on
keys
> and values (and work the JDKs before 5.0).
>
> To that end I wrote my own versions (trivial, to be sure, for HashMap,
> TreeMap, HashSet, TreeSet, ArrayList and LinkedList) but hate reinventing
the
> wheel.  So I have two questions:
>
> 1. Are there such things already and I just missed them.
> 2. If there are not, could I donate my implementations to commons
collections?
>
> My second question is quite selfish: I don't want to maintain separate
code
> bases every place I work (client or permanent) for such basic classes, and
> would love to have them in a more public, copyright/license-safe place
such
> as Jakarta.  (If the LGPL is an issue, I have no problem with relicensing
> with the Apache license or whatever other open source license is
> appropriate.)
>
> A sample class is appended below to illustrate what I mean exactly.  I
have
> full unit tests for all and maven builds.  I don't want to be accused of
> writing crapware.  :-)
>
>
> Cheers,
> --binkley
>
>
> Note -- this is for JDK 5, but JDK 1.4 versions are virtually identical,
just
> without the generics or annotations.
>
> /*
>  * Util - JDK collections extensions
>  * Copyright (C) 2004 B. K. Oxley (binkley) <bi...@alumni.rice.edu>
>  *
>  * This library is free software; you can redistribute it and/or
>  * modify it under the terms of the GNU Lesser General Public License
>  * as published by the Free Software Foundation; either version 2.1 of
>  * the License, or (at your option) any later version.
>  *
>  * This library is distributed in the hope that it will be useful, but
>  * WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>  * Lesser General Public License for more details.
>  *
>  * You should have received a copy of the GNU Lesser General Public
>  * License along with this library; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>  * USA
>  */
>
> package util;
>
> import java.util.HashMap;
> import java.util.Map;
>
> /**
>  * <p>Provides a {@link Map} which forbids <code>null</code> keys and
values
> and
>  * requires that keys and values be of certain types.  This handles the
large
>  * majority of uses which map definite types (in contrast to generic
>  * <code>Object</code> types) and which expect definite key and value
> instances
>  * (in contrast to <code>null</code>).</p>
>  *
>  * <p>All operations which take a key or a value, therefore, throw
>  * <code>NullPointerException</code> if the argument is <code>null</code>,
or
>  * throw <code>ClassCastException</code> if the argument is the wrong
> type.</p>
>  *
>  * <p>Of note is one particular common idiom:</p> <pre>
>  * if (null == map.get(key)) {
>  *     doSomethingForMissingKey(key);
>  * }</pre>
>  *
>  * <p>which will create a <code>null</code> value for <var>key</var> and
> return
>  * it. <code>SafeHashMap</code> requires the more correct:</p> <pre>
>  * if (!map.contains(key)) {
>  *     doSomethingForMissingKey(key);
>  * }</pre>
>  *
>  * <p>Typical use:</p> <pre>
>  * final SafeHashMap propertyMap = new SafeHashMap(String.class,
>  * Value.class);
>  * propertyMap.insert("testActual", new Value());
>  * propertyMap.insert("bar", new SubSomething()); // subclasses ok
>  * propertyMap.insert(null, new Value()); // NullPointerException
>  * propertyMap.insert("testActual", new Integer(3)); //
>  * ClassCastException</pre>
>  *
>  * <p>Limitations with JDK 5 generics prevent the more obvious
definition:</p>
>  * <pre>
>  * public SafeHashMap(final Class<K> keyClass, final Class<V> valueClass)
{
>  *     // ...
>  * }</pre> <p>As this makes impossible the straight-forward:</p> <pre>
>  * public SafeHashMap() {
>  *     this(Object.class, Object.class);
>  * }</pre>
>  *
>  * @author <a href="binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
>  * @version 1.0
>  */
> public class SafeHashMap <K, V> extends HashMap<K, V> {
>     private final Class keyClass;
>     private final Class valueClass;
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> which accepts any class
of
> key
>      * and value.
>      *
>      * @see SafeHashMap#SafeHashMap(Class, Class)
>      */
>     public SafeHashMap() {
>         this(Object.class, Object.class);
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> for a given
> <var>keyClass</var>
>      * and <var>valueClass</var>.
>      *
>      * @param keyClass the superclass of map keys
>      * @param valueClass the superclass of map values
>      *
>      * @see HashMap#HashMap()
>      */
>     public SafeHashMap(final Class keyClass, final Class valueClass) {
>         if (null == keyClass) throw new NullPointerException();
>         if (null == valueClass) throw new NullPointerException();
>
>         this.keyClass = keyClass;
>         this.valueClass = valueClass;
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> which accepts any class
of
> key
>      * and value with the given <var>initialCapacity</var>.
>      *
>      * @param initialCapacity the initial capacity
>      *
>      * @see SafeHashMap#SafeHashMap(Class, Class, int)
>      */
>     public SafeHashMap(final int initialCapacity) {
>         this(Object.class, Object.class, initialCapacity);
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> for a given
> <var>keyClass</var>
>      * and <var>valueClass</var> with the given
<var>initialCapacity</var>.
>      *
>      * @param keyClass the superclass of map keys
>      * @param valueClass the superclass of map values
>      * @param initialCapacity the initial capacity
>      *
>      * @see HashMap#HashMap(int)
>      */
>     public SafeHashMap(final Class keyClass, final Class valueClass,
>             final int initialCapacity) {
>         super(initialCapacity);
>
>         if (null == keyClass) throw new NullPointerException();
>         if (null == valueClass) throw new NullPointerException();
>
>         this.keyClass = keyClass;
>         this.valueClass = valueClass;
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> which accepts any class
of
> key
>      * and value with the given <var>initialCapacity</var> and
>      * <var>loadFactor</var>.
>      *
>      * @param initialCapacity the initial capacity
>      * @param loadFactor the load factor
>      *
>      * @see SafeHashMap#SafeHashMap(Class, Class, int, float)
>      */
>     public SafeHashMap(final int initialCapacity, final float loadFactor)
{
>         this(Object.class, Object.class, initialCapacity, loadFactor);
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> for a given
> <var>keyClass</var>
>      * and <var>valueClass</var> with the given <var>initialCapacity</var>
and
>      * <var>loadFactor</var>.
>      *
>      * @param keyClass the superclass of map keys
>      * @param valueClass the superclass of map values
>      * @param initialCapacity the initial capacity
>      * @param loadFactor the load factor
>      *
>      * @see HashMap#HashMap(int, float)
>      */
>     public SafeHashMap(final Class keyClass, final Class valueClass,
>             final int initialCapacity, final float loadFactor) {
>         super(initialCapacity, loadFactor);
>
>         if (null == keyClass) throw new NullPointerException();
>         if (null == valueClass) throw new NullPointerException();
>
>         this.keyClass = keyClass;
>         this.valueClass = valueClass;
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> which accepts any class
of
> key
>      * and value with the given <var>map</var>.
>      *
>      * @param map the map
>      *
>      * @see SafeHashMap#SafeHashMap(Class, Class, Map)
>      */
>     public SafeHashMap(final Map<? extends K, ? extends V> map) {
>         this(Object.class, Object.class, map);
>     }
>
>     /**
>      * Constructs a new <code>SafeHashMap</code> for a given
> <var>keyClass</var>
>      * and <var>valueClass</var> with the given <var>map</var>.
>      *
>      * @param keyClass the superclass of map keys
>      * @param valueClass the superclass of map values
>      * @param map the map
>      *
>      * @see HashMap#HashMap(Map)
>      */
>     public SafeHashMap(final Class keyClass, final Class valueClass,
>             final Map<? extends K, ? extends V> map) {
>         super(map);
>
>         if (null == keyClass) throw new NullPointerException();
>         if (null == valueClass) throw new NullPointerException();
>
>         this.keyClass = keyClass;
>         this.valueClass = valueClass;
>     }
>
>     /**
>      * {@inheritDoc}
>      */
>     @Override public boolean containsKey(final Object key) {
>         validateKey(key);
>
>         return super.containsKey(key);
>     }
>
>     /**
>      * {@inheritDoc}
>      */
>     @Override public boolean containsValue(final Object value) {
>         validateValue(value);
>
>         return super.containsValue(value);
>     }
>
>     /**
>      * {@inheritDoc}
>      *
>      * @throws IllegalArgumentException if <var>key</var> is missing
>      */
>     @Override public V get(final Object key) {
>         validateKey(key);
>
>         if (!containsKey(key)) throw new IllegalArgumentException();
>
>         return super.get(key);
>     }
>
>     /**
>      * {@inheritDoc}
>      *
>      * @throws IllegalArgumentException if <var>key</var> is duplicate
>      */
>     @Override public V put(final K key, final V value) {
>         validateKey(key);
>         validateValue(value);
>
>         if (containsKey(key)) throw new IllegalArgumentException();
>
>         return super.put(key, value);
>     }
>
>     /**
>      * Sets the entry having the given <var>key</var> with
<var>value</var>.
>      *
>      * @param key the map key
>      * @param value the map value
>      *
>      * @return the previous value
>      *
>      * @throws IllegalArgumentException if <var>key</var> is missing
>      */
>     public V set(final K key, final V value) {
>         validateKey(key);
>         validateValue(value);
>
>         if (!containsKey(key)) throw new IllegalArgumentException();
>
>         return super.put(key, value);
>     }
>
>     /**
>      * {@inheritDoc}
>      *
>      * @throws IllegalArgumentException if <var>key</var> is missing
>      */
>     @Override public V remove(final Object key) {
>         validateKey(key);
>
>         if (!containsKey(key)) throw new IllegalArgumentException();
>
>         return super.remove(key);
>     }
>
>     private void validateKey(final Object key) {
>         if (null == key) throw new NullPointerException();
>         if (!keyClass.isAssignableFrom(key.getClass()))
>             throw new ClassCastException();
>     }
>
>     private void validateValue(final Object v) {
>         if (null == v) throw new NullPointerException();
>         if (!valueClass.isAssignableFrom(v.getClass()))
>             throw new ClassCastException();
>     }
> }
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-user-help@jakarta.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-user-help@jakarta.apache.org