code: 
  package cc.mallet.util;
  
  import java.util.Iterator;
  import java.util.HashSet;
  import java.util.HashMap;
  import java.io.*;
  
  public class PropertyList implements Serializable
  {
  
  	protected PropertyList next;
  	protected String key;
  
  	public static PropertyList add (String key, Object value,
  																	PropertyList rest)
  	{
  		assert (key != null);
  		return new ObjectProperty (key, value, rest);
  	}
  
  	public static PropertyList add (String key, String value,
  																	PropertyList rest)
  	{
  		assert (key != null);
  		return new ObjectProperty (key, value, rest);
  	}
  	
  	public static PropertyList add (String key, double value,
  																	PropertyList rest)
  	{
  		assert (key != null);
  		return new NumericProperty (key, value, rest);
  	}
  
  	public static PropertyList remove (String key, 
  																		 PropertyList rest)
  	{
  		assert (key != null);
  		return new ObjectProperty (key, null, rest);
  	}
  
  	
  	public Object lookupObject (String key)
  	{
  		if (this.key.equals (key)) {
  			if (this instanceof ObjectProperty)
  				return ((ObjectProperty)this).value;
  			else if (this instanceof NumericProperty)
  				return new Double(((NumericProperty)this).value);
  			else
  				throw new IllegalStateException ("Unrecognitized PropertyList entry.");
  		} else if (this.next == null) {
  			return null;
  		} else {
  			return next.lookupObject (key);
  		}
  	}
  
  	public double lookupNumber (String key)
  	{
  		if (this.key.equals (key)) {
  			if (this instanceof NumericProperty)
  				return ((NumericProperty)this).value;
  			else if (this instanceof ObjectProperty) {
  				Object obj = ((ObjectProperty)this).value;
  				if (obj == null) return 0;
  				// xxx Remove these?  Use might ask for numericIterator expecting to get these (and not!)
  				if (obj instanceof Double) return ((Double)obj).doubleValue();
  				if (obj instanceof Integer) return ((Double)obj).intValue();
  				if (obj instanceof Float) return ((Double)obj).floatValue();
  				if (obj instanceof Short) return ((Double)obj).shortValue();
  				if (obj instanceof Long) return ((Double)obj).longValue();
  				// xxx? throw new IllegalStateException ("Property is not numeric.");
  				return 0;
  			} else
  				throw new IllegalStateException ("Unrecognitized PropertyList entry.");
  		} else if (this.next == null) {
  			return 0;
  		} else {
  			return next.lookupNumber (key);
  		}
  	}
  
  	public boolean hasProperty (String key)
  	{
  		if (this.key.equals (key)) {
  			if (this instanceof ObjectProperty && ((ObjectProperty)this).value == null)
  				return false;
  			else
  				return true;
  		} else if (this.next == null) {
  			return false;
  		} else {
  			return next.hasProperty (key);
  		}
  	}
  	
  	public Iterator iterator ()
  	{
  		return new Iterator (this);
  	}
  	
  	public static PropertyList sumDuplicateKeyValues (PropertyList pl ) {
  		return sumDuplicateKeyValues(pl,false);
  	}
  
  	// culotta 2/02/04: to increment counts of properties values.
  	public static PropertyList sumDuplicateKeyValues (PropertyList pl, boolean ignoreZeros) {
  		if (!(pl instanceof NumericProperty))
  			throw new IllegalArgumentException ("PropertyList must be Numeric to sum values");
  		HashMap key2value = new HashMap ();
  		Iterator iter = pl.numericIterator();
  		while (iter.hasNext()) {
  			iter.nextProperty ();
  			String key = iter.getKey();
  			double val = iter.getNumericValue();
  			Double storedValue = (Double)key2value.get (key);
  			if (storedValue == null)
  				key2value.put (key, new Double (val));
  			else // sum stored value with current value
  				key2value.put (key, new Double (storedValue.doubleValue() + val));
  		}
  		PropertyList ret = null;
  		java.util.Iterator hashIter = key2value.keySet().iterator();
  		while (hashIter.hasNext()) { // create new property list
  			String key = (String) hashIter.next();
  			double val = ((Double)key2value.get (key)).doubleValue();
  			if(ignoreZeros && val==0.0)
  				continue;
  			ret = PropertyList.add (key, val, ret);
  		}
  		return ret;
  	}
  	
  	public Iterator numericIterator ()
  	{
  		return new NumericIterator (this);
  	}
  
  	public Iterator objectIterator ()
  	{
  		return new ObjectIterator (this);
  	}
  	
  	protected PropertyList ()
  	{
  		throw new IllegalArgumentException ("Zero args constructor not allowed.");
  	}
  	
  	protected PropertyList (String key, PropertyList rest)
  	{
  		this.key = key;
  		this.next = rest;
  	}
  
  	public void print ()
  	{
  		if (this instanceof NumericProperty)
  			System.out.println (this.key.toString() + "=" + ((NumericProperty)this).value);
  		else if (this instanceof ObjectProperty)
  			System.out.println (this.key.toString() + "=" + ((ObjectProperty)this).value);
  		else
  			throw new IllegalArgumentException ("Unrecognized PropertyList type");
  		if (this.next != null)
  			this.next.print();
  	}
  
  	// Serialization 
  	// PropertyList
  	
  	private static final long serialVersionUID = 1;
  	private static final int CURRENT_SERIAL_VERSION = 0;
  	
  	private void writeObject (ObjectOutputStream out) throws IOException {
  		out.writeInt (CURRENT_SERIAL_VERSION);
  		out.writeObject(next);
  		out.writeObject(key);
  	}
  	
  	private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
  		int version = in.readInt ();
  		next = (PropertyList) in.readObject();
  		key = (String) in.readObject();
  	}
  
    public int size ()
    {
      PropertyList pl = this;
      int size = 1;
      while (pl.next != null) {
        pl = pl.next;
        size++;
      }
      return size;
    }
  
  
    private static class NumericProperty extends PropertyList implements Serializable
    {
      protected double value;
  
      public NumericProperty (String key, double value, PropertyList rest)
      {
        super (key, rest);
        this.value = value;
      }
  
      // Serialization
      // NumericProperty
      private static final long serialVersionUID = 1;
      private static final int CURRENT_SERIAL_VERSION = 0;
  
  
  
      private void writeObject (ObjectOutputStream out) throws IOException {
        out.writeInt(CURRENT_SERIAL_VERSION);
        out.writeDouble(value);
      }
  
      private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
        int version = in.readInt();
        value = in.readDouble();
      }
  
    }
  
  	private static class ObjectProperty extends PropertyList
  	{
  		protected Object value;
  
  		public ObjectProperty (String key, Object value, PropertyList rest)
  		{
  			super (key, rest);
  			this.value = value;
  		}
  		// Serialization 
  		// ObjectProperty
  		
  		private static final long serialVersionUID = 1;
  		private static final int CURRENT_SERIAL_VERSION = 0;
  
  		private void writeObject (ObjectOutputStream out) throws IOException {
  			out.writeInt(CURRENT_SERIAL_VERSION);
  			out.writeObject(value);
  		}
  		
  		private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
  		  int version = in.readInt();
  			value = (Object) in.readObject();
  		}
  
  	}
  
  	public class Iterator implements java.util.Iterator, Serializable
  	{
  		PropertyList property, nextProperty;
  		HashSet deletedKeys = null;
  		boolean nextCalled = false;
  		boolean returnNumeric = true;
  		boolean returnObject = true;
  
  		public Iterator (PropertyList pl)
  		{
  			property = findReturnablePropertyAtOrAfter (pl);
  			if (property == null)
  				nextProperty = null;
  			else
  				nextProperty = findReturnablePropertyAtOrAfter (property.next);
  		}
  
  		private PropertyList findReturnablePropertyAtOrAfter (PropertyList property)
  		{
  			while (property != null) {
  				if (property instanceof NumericProperty && returnNumeric) {
  					if (((NumericProperty)property).value == 0.0) {
  						if (deletedKeys == null) deletedKeys = new HashSet();
  						deletedKeys.add (property.key);
  						property = property.next;
  					} else
  						break;
  				} else if (property instanceof ObjectProperty && returnObject) {
  					if (((ObjectProperty)property).value == null) {
  						if (deletedKeys == null) deletedKeys = new HashSet();
  						deletedKeys.add (property.key);
  						property = property.next;
  					} else
  						break;
  				} else
  					throw new IllegalStateException ("Unrecognized property type "+property.getClass().getName());
  			}
  			return property;
  		}
  		
  		public boolean hasNext ()
  		{
  			return ((nextCalled && nextProperty != null) || (!nextCalled && property != null));
  		}
  
  		public boolean isNumeric ()
  		{
  			return (property instanceof NumericProperty);
  		}
  
  		public double getNumericValue ()
  		{
  			return ((NumericProperty)property).value;
  		}
  
  		public Object getObjectValue ()
  		{
  			return ((ObjectProperty)property).value;
  		}
  		
  		public String getKey ()
  		{
  			return property.key;
  		}
  		
  		public PropertyList nextProperty ()
  		{
  			if (nextCalled) {
  				property = nextProperty;
  				nextProperty = findReturnablePropertyAtOrAfter (property.next);
  			} else
  				nextCalled = true;
  			return property;
  		}
  
  		public Object next ()
  		{
  			return nextProperty ();
  		}
  		
  		public void remove ()
  		{
  			throw new UnsupportedOperationException ();
  		}
  		
  			// Serialization 
  		// Iterator
  	
  		private static final long serialVersionUID = 1;
  		private static final int CURRENT_SERIAL_VERSION = 0;
  		
  		private void writeObject (ObjectOutputStream out) throws IOException {
  			out.writeInt (CURRENT_SERIAL_VERSION);
  			out.writeObject(property);
  			out.writeObject(nextProperty);
  			out.writeObject(deletedKeys);
  			out.writeBoolean(nextCalled);
  			out.writeBoolean(returnNumeric);
  			out.writeBoolean(returnObject);
  		}
  		
  		private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
  			int version = in.readInt ();
  			property = (PropertyList) in.readObject();
  			nextProperty = (PropertyList) in.readObject();
  			deletedKeys = (HashSet) in.readObject();
  			nextCalled = in.readBoolean();
  			returnNumeric = in.readBoolean();
  			returnObject = in.readBoolean();
  		}
  
  	}
  
  
  	public class NumericIterator extends Iterator implements Serializable
  	{
  		public NumericIterator (PropertyList pl) {
  			super (pl);
  			this.returnObject = false;
  		}
  	}
  
  	public class ObjectIterator extends Iterator implements Serializable
  	{
  		public ObjectIterator (PropertyList pl) {
  			super (pl);
  			this.returnNumeric = false;
  		}
  	}
  
      // for fast merging of PropertLists
      //  gmann 8/14/6
      public PropertyList last(){
  	if (next == null){
  	    return this;
  	}
  	else return next.last();
      }
  
      public PropertyList append(PropertyList nextPl) throws UnsupportedOperationException{
  	if (this.next != null){
  	    throw new UnsupportedOperationException("PropertyList.java: Cannot append to middle of a list\n");
  	}
  	
  	this.next = nextPl;
  	return last();
      }
  	
  }
  