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