diff --git a/jaxen/src/java/main/org/jaxen/BaseXPath.java b/jaxen/src/java/main/org/jaxen/BaseXPath.java
index 4d54e01..c776199 100644
--- a/jaxen/src/java/main/org/jaxen/BaseXPath.java
+++ b/jaxen/src/java/main/org/jaxen/BaseXPath.java
@@ -50,6 +50,7 @@ package org.jaxen;
 
 import java.io.Serializable;
 import java.util.List;
+import java.util.Iterator;
 
 import org.jaxen.expr.Expr;
 import org.jaxen.expr.XPathExpr;
@@ -172,14 +173,18 @@ public class BaseXPath implements XPath, Serializable
      */
     public Object evaluate(Object context) throws JaxenException
     {
+        try
+        {
         List answer = selectNodes(context);
 
-        if ( answer != null
-             &&
-             answer.size() == 1 )
+            if ( answer != null ) {
+                Iterator answerIter = answer.iterator();
+                if (answerIter.hasNext())
         {
-            Object first = answer.get(0);
+                    Object first = answerIter.next();
 
+                    if (!answerIter.hasNext())
+                    {
             if ( first instanceof String
                  ||
                  first instanceof Number
@@ -189,8 +194,19 @@ public class BaseXPath implements XPath, Serializable
                 return first;
             }
         }
+                }
+            }
         return answer;
     }
+        catch (JaxenRuntimeException jre)
+        {
+            Throwable cause = jre.getCause();
+            if (cause instanceof JaxenException) {
+                throw (JaxenException) cause;
+            }
+            throw jre;
+        }
+    }
     
     /** Select all nodes that are selected by this XPath
      *  expression. If multiple nodes match, multiple nodes
@@ -674,8 +690,19 @@ public class BaseXPath implements XPath, Serializable
      */
     protected List selectNodesForContext(Context context) throws JaxenException
     {
+        try
+        {
         List list = this.xpath.asList( context );
         return list;
+        }
+        catch (JaxenRuntimeException jre)
+        {
+            Throwable cause = jre.getCause();
+            if (cause instanceof JaxenException) {
+                throw (JaxenException) cause;
+            }
+            throw jre;
+        }
         
     }
  
diff --git a/jaxen/src/java/main/org/jaxen/Context.java b/jaxen/src/java/main/org/jaxen/Context.java
index bc17075..28a0b72 100644
--- a/jaxen/src/java/main/org/jaxen/Context.java
+++ b/jaxen/src/java/main/org/jaxen/Context.java
@@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 import java.io.Serializable;
-import java.util.ArrayList;
+import org.jaxen.util.LazyList;
 import java.util.Collections;
 import java.util.List;
 
@@ -86,6 +86,12 @@ public class Context implements Serializable {
     /** Current context position */
     private int position;
 
+    /** Whether the size has been explicitly set. */
+    private boolean needSize = true;
+
+    /** Whether the position needs to be compared against the size. False if position is explicitly set. */
+    private boolean needPosition = true;
+
     // ----------------------------------------------------------------------
     //     Constructors
     // ----------------------------------------------------------------------
@@ -100,6 +106,8 @@ public class Context implements Serializable {
         this.nodeSet        = Collections.EMPTY_LIST;
         this.size           = 0;
         this.position       = 0;
+        this.needSize = false;
+        this.needPosition = false;
     }
     
     // ----------------------------------------------------------------------
@@ -125,8 +133,8 @@ public class Context implements Serializable {
     public void setNodeSet(List nodeSet)
     {
         this.nodeSet = nodeSet;
-        this.size    = nodeSet.size();
-        if (position >= size) this.position = 0;
+        this.needSize = true;
+        this.needPosition = true;
     }
 
     /** Retrieve the context node-set.
@@ -229,6 +237,7 @@ public class Context implements Serializable {
     public void setSize(int size)
     {
         this.size = size;
+        this.needSize = false;
     }
 
     /** Retrieve the size of the current context node-set.
@@ -237,6 +246,9 @@ public class Context implements Serializable {
      */
     public int getSize()
     {
+        if (this.needSize) {
+            setSize(this.nodeSet.size());
+        }
         return this.size;
     }
 
@@ -247,6 +259,7 @@ public class Context implements Serializable {
     public void setPosition(int position)
     {
         this.position = position;
+        this.needPosition = false;
     }
 
     /** Retrieve current position in the context node-set.
@@ -255,6 +268,10 @@ public class Context implements Serializable {
      */
     public int getPosition()
     {
+        if (this.needPosition) {
+            if (this.position >= getSize()) this.position = 0;
+            this.needPosition = false;
+        }
         return this.position;
     }
 
@@ -274,10 +291,9 @@ public class Context implements Serializable {
 
         if ( thisNodeSet != null )
         {
-            List dupeNodeSet = new ArrayList( thisNodeSet.size() );
-            dupeNodeSet.addAll( thisNodeSet );
+            List dupeNodeSet = new LazyList( thisNodeSet.iterator() );
             dupe.setNodeSet( dupeNodeSet );
-            dupe.setPosition(this.position);
+            dupe.setPosition( getPosition() );
         }
 
         return dupe;
diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java b/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java
index ae6014e..329dae4 100644
--- a/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java
+++ b/jaxen/src/java/main/org/jaxen/expr/DefaultFilterExpr.java
@@ -53,6 +53,7 @@ import java.util.ArrayList;
 import java.util.List;
 import org.jaxen.Context;
 import org.jaxen.JaxenException;
+import org.jaxen.util.LazyList;
 
 /**
  * @deprecated this class will become non-public in the future;
@@ -143,9 +144,7 @@ public class DefaultFilterExpr extends DefaultExpr implements FilterExpr, Predic
         else
         {
             List nodeSet = context.getNodeSet();
-            ArrayList list = new ArrayList(nodeSet.size());
-            list.addAll( nodeSet );
-            results = list;
+            results = new LazyList(nodeSet.iterator());
         }
         
         if ( results instanceof Boolean ) 
diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java b/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java
index 951d722..96375a9 100644
--- a/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java
+++ b/jaxen/src/java/main/org/jaxen/expr/DefaultLocationPath.java
@@ -56,6 +56,7 @@ import java.util.List;
 import org.jaxen.Context;
 import org.jaxen.ContextSupport;
 import org.jaxen.JaxenException;
+import org.jaxen.util.LazyList;
 
 abstract class DefaultLocationPath extends DefaultExpr implements LocationPath
 {
@@ -129,7 +130,10 @@ abstract class DefaultLocationPath extends DefaultExpr implements LocationPath
     public Object evaluate(Context context) throws JaxenException
     {
         List nodeSet = context.getNodeSet();
-        List contextNodeSet = new ArrayList(nodeSet);
+        if (nodeSet.isEmpty()) {
+            return nodeSet;
+        }
+        List contextNodeSet = new LazyList(nodeSet.iterator());
         ContextSupport support = context.getContextSupport();
         Context stepContext = new Context(support);
         Iterator stepIter = getSteps().iterator();
@@ -138,13 +142,20 @@ abstract class DefaultLocationPath extends DefaultExpr implements LocationPath
             Step eachStep = (Step) stepIter.next();
             stepContext.setNodeSet(contextNodeSet);
             contextNodeSet = eachStep.evaluate(stepContext);
+            if (contextNodeSet.isEmpty()) {
+                return contextNodeSet;
+            }
             // now we need to reverse the list if this is a reverse axis
             if (isReverseAxis(eachStep)) {
+                // todo fixme
                 Collections.reverse(contextNodeSet);
             }
         }
         
         if (getSteps().size() > 1) {
+            // todo fixme. This is the only source of calls to LazyList.fill().
+            // The nodes being sorted here are usually already sorted. When is this
+            // true, so that we can flag that the sort should not happen?
             Collections.sort(contextNodeSet, new NodeComparator(support.getNavigator()));
         }
         
diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java b/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java
index 31baf81..6bdca20 100644
--- a/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java
+++ b/jaxen/src/java/main/org/jaxen/expr/DefaultNameStep.java
@@ -33,8 +33,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 package org.jaxen.expr;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -42,9 +40,11 @@ import org.jaxen.Context;
 import org.jaxen.ContextSupport;
 import org.jaxen.JaxenException;
 import org.jaxen.UnresolvableException;
+import org.jaxen.UnsupportedAxisException;
 import org.jaxen.Navigator;
 import org.jaxen.expr.iter.IterableAxis;
 import org.jaxen.saxpath.Axis;
+import org.jaxen.util.LazyList;
 
 /** 
  * Expression object that represents any flavor
@@ -155,157 +155,39 @@ public class DefaultNameStep extends DefaultStep implements NameStep {
      */
     public List evaluate(Context context) throws JaxenException {
 
-        List contextNodeSet  = context.getNodeSet();
-        int contextSize = contextNodeSet.size();
-        // optimize for context size 0
-        if (contextSize == 0) {
-            return Collections.EMPTY_LIST;
-        }
-        ContextSupport support = context.getContextSupport();
-        IterableAxis iterableAxis = getIterableAxis();
+        final ContextSupport support = context.getContextSupport();
+        final IterableAxis iterableAxis = getIterableAxis();
+        final DefaultNameStep namestep = this;
         boolean namedAccess = (!matchesAnyName && iterableAxis.supportsNamedAccess(support));
         
-        // optimize for context size 1 (common case, avoids lots of object creation)
-        if (contextSize == 1) {
-            Object contextNode = contextNodeSet.get(0);
-            if (namedAccess) {
-                // get the iterator over the nodes and check it
-                String uri = null;
-                if (hasPrefix) {
-                    uri = support.translateNamespacePrefixToUri(prefix);
-                    if (uri == null) {
-                        throw new UnresolvableException("XPath expression uses unbound namespace prefix " + prefix);
-                    }
-                }
-                Iterator axisNodeIter = iterableAxis.namedAccessIterator(
-                                contextNode, support, localName, prefix, uri);
-                if (axisNodeIter == null || !axisNodeIter.hasNext()) {
-                    return Collections.EMPTY_LIST;
-                }
-
-                // convert iterator to list for predicate test
-                // no need to filter as named access guarantees this
-                List newNodeSet = new ArrayList();
-                while (axisNodeIter.hasNext()) {
-                    newNodeSet.add(axisNodeIter.next());
-                }
-                
-                // evaluate the predicates
-                return getPredicateSet().evaluatePredicates(newNodeSet, support);
-                
-            } 
-            else {
-                // get the iterator over the nodes and check it
-                Iterator axisNodeIter = iterableAxis.iterator(contextNode, support);
-                if (axisNodeIter == null || !axisNodeIter.hasNext()) {
-                    return Collections.EMPTY_LIST;
-                }
-
-                // run through iterator, filtering using matches()
-                // adding to list for predicate test
-                List newNodeSet = new ArrayList(contextSize);
-                while (axisNodeIter.hasNext()) {
-                    Object eachAxisNode = axisNodeIter.next();
-                    if (matches(eachAxisNode, support)) {
-                        newNodeSet.add(eachAxisNode);
-                    }
-                }
-                
-                // evaluate the predicates
-                return getPredicateSet().evaluatePredicates(newNodeSet, support);
-            }
-        }
-
-        // full case
-        IdentitySet unique = new IdentitySet();
-        List interimSet = new ArrayList(contextSize);
-        List newNodeSet = new ArrayList(contextSize);
-        
-        if (namedAccess) {
-            String uri = null;
-            if (hasPrefix) {
-                uri = support.translateNamespacePrefixToUri(prefix);
-                if (uri == null) {
-                    throw new UnresolvableException("XPath expression uses unbound namespace prefix " + prefix);
-                }
-            }
-            for (int i = 0; i < contextSize; ++i) {
-                Object eachContextNode = contextNodeSet.get(i);
-
-                Iterator axisNodeIter = iterableAxis.namedAccessIterator(
-                                eachContextNode, support, localName, prefix, uri);
-                if (axisNodeIter == null || !axisNodeIter.hasNext()) {
-                    continue;
-                }
-
-				while (axisNodeIter.hasNext())
+        if (!namedAccess)
 				{
-					Object eachAxisNode = axisNodeIter.next();
-					interimSet.add(eachAxisNode);
+            return super.evaluate(context);
 				}
 
-				// evaluate the predicates
-				List predicateNodes = getPredicateSet().evaluatePredicates(interimSet, support);
-
-				// ensure only one of each node in the result
-				Iterator predicateNodeIter = predicateNodes.iterator();
-				while (predicateNodeIter.hasNext())
+        String uri = null;
+        if (hasPrefix)
 				{
-					Object eachPredicateNode = predicateNodeIter.next();
-					if (! unique.contains(eachPredicateNode))
+            uri = support.translateNamespacePrefixToUri(prefix);
+            if (uri == null)
 					{
-						unique.add(eachPredicateNode);
-						newNodeSet.add(eachPredicateNode);
-					}
-				}
-				interimSet.clear();
-			}
-            
-        } else {
-            for (int i = 0; i < contextSize; ++i) {
-                Object eachContextNode = contextNodeSet.get(i);
-
-                Iterator axisNodeIter = axisIterator(eachContextNode, support);
-                if (axisNodeIter == null || !axisNodeIter.hasNext()) {
-                    continue;
-                }
-
-                /* See jaxen-106. Might be able to optimize this by doing
-                 * specific matching for individual axes. For instance on namespace axis
-                 * we should only get namespace nodes and on attribute axes we only get 
-                 * attribute nodes. Self and parent axes have single members.
-                 * Children, descendant, ancestor, and sibling axes never 
-                 * see any attributes or namespaces
-                 */
-                
-                // ensure only unique matching nodes in the result
-                while (axisNodeIter.hasNext()) {
-                    Object eachAxisNode = axisNodeIter.next();
-
-                    if (matches(eachAxisNode, support)) {
-						interimSet.add(eachAxisNode);
+                throw new UnresolvableException("XPath expression uses unbound namespace prefix " + prefix);
                     }
                 }
-
-                // evaluate the predicates
-				List predicateNodes = getPredicateSet().evaluatePredicates(interimSet, support);
-
-				// ensure only one of each node in the result
-				Iterator predicateNodeIter = predicateNodes.iterator();
-				while (predicateNodeIter.hasNext())
+        final String finalUri = uri;
+        // full case
+        List contextNodeSet  = context.getNodeSet();
+        final Iterator contextIterator = contextNodeSet.iterator();
+        Iterator predicateIterator = new AbstractPredicateIterator(contextIterator, namestep, support)
 				{
-					Object eachPredicateNode = predicateNodeIter.next();
-					if (! unique.contains(eachPredicateNode))
+            protected Iterator axisNodeIterator(Object eachContextNode) throws UnsupportedAxisException
 					{
-						unique.add(eachPredicateNode);
-						newNodeSet.add(eachPredicateNode);
-					}
-				}
-                interimSet.clear();
-            }
+                return iterableAxis.namedAccessIterator(
+                        eachContextNode, support, localName, prefix, finalUri);
         }
+        };
         
-        return newNodeSet;
+        return new LazyList(predicateIterator);
     }
     
     /**
diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultStep.java b/jaxen/src/java/main/org/jaxen/expr/DefaultStep.java
index 50ab6ca..3c947ab 100644
--- a/jaxen/src/java/main/org/jaxen/expr/DefaultStep.java
+++ b/jaxen/src/java/main/org/jaxen/expr/DefaultStep.java
@@ -46,14 +46,19 @@
  */
 package org.jaxen.expr;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 import org.jaxen.Context;
 import org.jaxen.ContextSupport;
+import org.jaxen.JaxenConstants;
 import org.jaxen.JaxenException;
+import org.jaxen.JaxenRuntimeException;
 import org.jaxen.UnsupportedAxisException;
+import org.jaxen.util.FilterIterator;
+import org.jaxen.util.LazyList;
 import org.jaxen.expr.iter.IterableAxis;
 import org.jaxen.saxpath.Axis;
 
@@ -126,46 +131,101 @@ public abstract class DefaultStep implements Step
     public List evaluate(final Context context) throws JaxenException
     {
         final List contextNodeSet  = context.getNodeSet();
-        final IdentitySet unique = new IdentitySet();
-        final int contextSize = contextNodeSet.size();
-
-        // ???? try linked lists instead?
-        // ???? initial size for these?
-        final ArrayList interimSet = new ArrayList();
-        final ArrayList newNodeSet = new ArrayList();
         final ContextSupport support = context.getContextSupport();
+        final Iterator contextIterator = contextNodeSet.iterator();
+        List newNodeSet;
+
+        Iterator predicateIterator = new AbstractPredicateIterator(contextIterator, this, support)
+        {
+            protected Iterator axisNodeIterator(final Object eachContextNode) throws UnsupportedAxisException
+            {
+                return new FilterIterator(axisIterator(eachContextNode, support))
+                {
+                    protected boolean matches(Object item) throws JaxenException
+                    {
+                        return step.matches(item, support);
+                    }
+                };
+            }
+        };
+        newNodeSet = new LazyList(predicateIterator);
+        return newNodeSet;
+    }
             
-        // ???? use iterator instead
-        for ( int i = 0 ; i < contextSize ; ++i )
+    protected static abstract class AbstractPredicateIterator implements Iterator
         {
-            Object eachContextNode = contextNodeSet.get( i );
+        protected IdentitySet unique = new IdentitySet();
+        protected Iterator source = JaxenConstants.EMPTY_ITERATOR;
+        protected Iterator contextIterator;
+        protected DefaultStep step;
+        protected ContextSupport support;
 
+        protected AbstractPredicateIterator(Iterator contextIterator, DefaultStep namestep, ContextSupport support)
+        {
+            this.contextIterator = contextIterator;
+            this.step = namestep;
+            this.support = support;
+        }
 
-                /* See jaxen-106. Might be able to optimize this by doing
-                 * specific matching for individual axes. For instance on namespace axis
-                 * we should only get namespace nodes and on attribute axes we only get 
-                 * attribute nodes. Self and parent axes have single members.
-                 * Children, descendant, ancestor, and sibling axes never 
-                 * see any attributes or namespaces
-                 */
-            Iterator axisNodeIter = axis.iterator(eachContextNode, support);
-            while ( axisNodeIter.hasNext() )
+        public Object next()
+        {
+            if (hasNext())
+            {
+                return source.next();
+            }
+
+            throw new NoSuchElementException();
+        }
+
+        public boolean hasNext()
             {
-                Object eachAxisNode = axisNodeIter.next();
-                if ( ! unique.contains( eachAxisNode ) )
+            try
                 {
-                    if ( matches( eachAxisNode, support ) )
+                if (source.hasNext())
                     {
-                        unique.add( eachAxisNode );
-                        interimSet.add( eachAxisNode );
+                    return true;
                     }
+
+                while (contextIterator.hasNext())
+                {
+                    Object eachContextNode = contextIterator.next();
+                    Iterator axisNodeIter = axisNodeIterator(eachContextNode);
+                    if (!axisNodeIter.hasNext())
+                    {
+                        continue;
                 }
+                    List interimSet = new LazyList(axisNodeIter);
+                    List predicateNodes = step.getPredicateSet().evaluatePredicates(interimSet, support);
+                    source = new FilterIterator(predicateNodes.iterator())
+                    {
+                        protected boolean matches(Object item)
+                        {
+                            if (!unique.contains(item))
+                            {
+                                unique.add(item);
+                                return true;
             }
-            newNodeSet.addAll(getPredicateSet().evaluatePredicates(
-                              interimSet, support ));
-            interimSet.clear();
+                            return false;
+                        }
+                    };
+                    if (source.hasNext())
+                    {
+                        return true;
+                    }
+                }
+                return false;
+            }
+            catch (JaxenException e)
+            {
+                throw new JaxenRuntimeException(e);
         }
-        return newNodeSet;
     }
 
+        protected abstract Iterator axisNodeIterator(final Object eachContextNode) throws UnsupportedAxisException;
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+    }
 }
diff --git a/jaxen/src/java/main/org/jaxen/expr/DefaultTruthExpr.java b/jaxen/src/java/main/org/jaxen/expr/DefaultTruthExpr.java
index 8354fe5..033709f 100644
--- a/jaxen/src/java/main/org/jaxen/expr/DefaultTruthExpr.java
+++ b/jaxen/src/java/main/org/jaxen/expr/DefaultTruthExpr.java
@@ -93,7 +93,7 @@ abstract class DefaultTruthExpr extends DefaultBinaryExpr
     
     protected boolean setIsEmpty( List set )
       {
-      return (set == null || set.size() == 0);
+      return (set == null || set.isEmpty());
       }
 
     protected boolean eitherIsBoolean(Object lhs,
diff --git a/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java b/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java
index 4118ae3..2abe8ac 100644
--- a/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java
+++ b/jaxen/src/java/main/org/jaxen/expr/PredicateSet.java
@@ -58,6 +58,8 @@ import java.util.List;
 import org.jaxen.Context;
 import org.jaxen.ContextSupport;
 import org.jaxen.JaxenException;
+import org.jaxen.util.FilterIterator;
+import org.jaxen.util.LazyList;
 import org.jaxen.function.BooleanFunction;
 
 /**
@@ -248,31 +250,33 @@ public class PredicateSet implements Serializable
         return nodes2Filter;
     }
    
-    public List applyPredicate(Predicate predicate, List nodes2Filter, ContextSupport support)
+    public List applyPredicate(final Predicate predicate, List nodes2Filter, final ContextSupport support)
             throws JaxenException {
         final int nodes2FilterSize = nodes2Filter.size();
-        List filteredNodes = new ArrayList(nodes2FilterSize);
-        // Set up a dummy context with a list to hold each node
-        Context predContext = new Context(support);
-        List tempList = new ArrayList(1);
-        predContext.setNodeSet(tempList);
         // loop through the current nodes to filter and add to the
         // filtered nodes list if the predicate succeeds
-        for (int i = 0; i < nodes2FilterSize; ++i) {
-            Object contextNode = nodes2Filter.get(i);
+        final Context predContext = new Context(support);
+        FilterIterator filtered = new FilterIterator(nodes2Filter.iterator()) {
+            private List tempList = new ArrayList(1);
+            private int i = 0;
+
+            protected boolean matches(Object contextNode) throws JaxenException
+            {
+                i++;
             tempList.clear();
             tempList.add(contextNode);
             predContext.setNodeSet(tempList);
             // ????
-            predContext.setPosition(i + 1);
+                predContext.setPosition(i);
             predContext.setSize(nodes2FilterSize);
             Object predResult = predicate.evaluate(predContext);
-            if (predResult instanceof Number) {
+                if (predResult instanceof Number)
+                {
                 // Here we assume nodes are in forward or reverse order
                 // as appropriate for axis
                 int proximity = ((Number) predResult).intValue();
-                if (proximity == (i + 1)) {
-                    filteredNodes.add(contextNode);
+                    if (proximity == i) {
+                        return true;
                 }
             }
             else {
@@ -280,11 +284,13 @@ public class PredicateSet implements Serializable
                     BooleanFunction.evaluate(predResult,
                                             predContext.getNavigator());
                 if (includes.booleanValue()) {
-                    filteredNodes.add(contextNode);
+                        return true;
                 }
             }
+                return false;
         }
-        return filteredNodes;
+        };
+        return new LazyList(filtered);
     }
     
 }
diff --git a/jaxen/src/java/main/org/jaxen/function/BooleanFunction.java b/jaxen/src/java/main/org/jaxen/function/BooleanFunction.java
index e9c1857..805d201 100644
--- a/jaxen/src/java/main/org/jaxen/function/BooleanFunction.java
+++ b/jaxen/src/java/main/org/jaxen/function/BooleanFunction.java
@@ -155,7 +155,7 @@ public class BooleanFunction implements Function
             List list = (List) obj;
             
             // if it's an empty list, then we have a null node-set -> false            
-            if (list.size() == 0)
+            if (list.isEmpty())
             {
                 return Boolean.FALSE;
             }
diff --git a/jaxen/src/java/main/org/jaxen/function/IdFunction.java b/jaxen/src/java/main/org/jaxen/function/IdFunction.java
index e0dc4c0..33bb8c6 100644
--- a/jaxen/src/java/main/org/jaxen/function/IdFunction.java
+++ b/jaxen/src/java/main/org/jaxen/function/IdFunction.java
@@ -128,7 +128,7 @@ public class IdFunction implements Function
      */
     public static List evaluate(List contextNodes, Object arg, Navigator nav)
     {
-        if (contextNodes.size() == 0) return Collections.EMPTY_LIST;
+        if (contextNodes.isEmpty()) return Collections.EMPTY_LIST;
       
         List nodes = new ArrayList();
 
diff --git a/jaxen/src/java/main/org/jaxen/function/ext/EvaluateFunction.java b/jaxen/src/java/main/org/jaxen/function/ext/EvaluateFunction.java
index 0936a45..d733a0b 100644
--- a/jaxen/src/java/main/org/jaxen/function/ext/EvaluateFunction.java
+++ b/jaxen/src/java/main/org/jaxen/function/ext/EvaluateFunction.java
@@ -80,7 +80,7 @@ public class EvaluateFunction implements Function
     {
         List contextNodes = context.getNodeSet();
         
-        if (contextNodes.size() == 0)
+        if (contextNodes.isEmpty())
             return Collections.EMPTY_LIST;
       
         Navigator nav = context.getNavigator();
diff --git a/jaxen/src/java/main/org/jaxen/util/FilterIterator.java b/jaxen/src/java/main/org/jaxen/util/FilterIterator.java
new file mode 100644
index 0000000..a522287
--- /dev/null
+++ b/jaxen/src/java/main/org/jaxen/util/FilterIterator.java
@@ -0,0 +1,122 @@
+package org.jaxen.util;
+
+import org.jaxen.JaxenException;
+import org.jaxen.JaxenRuntimeException;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/*
+ * $Header$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * Copyright (C) 2000-2005 bob mcwhirter & James Strachan.
+ * 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 disclaimer that follows
+ *    these conditions in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. The name "Jaxen" must not be used to endorse or promote products
+ *    derived from this software without prior written permission.  For
+ *    written permission, please contact license@jaxen.org.
+ *
+ * 4. Products derived from this software may not be called "Jaxen", nor
+ *    may "Jaxen" appear in their name, without prior written permission
+ *    from the Jaxen Project Management (pm@jaxen.org).
+ *
+ * In addition, we request (but do not require) that you include in the
+ * end-user documentation provided with the redistribution and/or in the
+ * software itself an acknowledgement equivalent to the following:
+ *     "This product includes software developed by the
+ *      Jaxen Project (http://www.jaxen.org/)."
+ * Alternatively, the acknowledgment may be graphical using the logos
+ * available at http://www.jaxen.org/
+ *
+ * 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 Jaxen AUTHORS OR THE PROJECT
+ * 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 Jaxen Project and was originally
+ * created by bob mcwhirter <bob@werken.com> and
+ * James Strachan <jstrachan@apache.org>.  For more information on the
+ * Jaxen Project, please see <http://www.jaxen.org/>.
+ *
+ * $Id$
+*/
+
+/**
+ * Filters another iterator's results.
+ */
+public abstract class FilterIterator implements Iterator
+{
+    private Iterator source;
+    private boolean pushed = false;
+    private Object pushback;
+
+    public FilterIterator(Iterator source)
+    {
+        this.source = source;
+    }
+
+    public boolean hasNext()
+    {
+        if (pushed) {
+            return true;
+        }
+        while (source.hasNext()) {
+            try
+            {
+                pushback = source.next();
+                if (matches(pushback)) {
+                    pushed = true;
+                    return true;
+                }
+            }
+            catch (JaxenException e)
+            {
+                throw new JaxenRuntimeException(e);
+            }
+        }
+        return false;
+    }
+
+    protected abstract boolean matches(Object item) throws JaxenException;
+
+    public Object next()
+    {
+        if (hasNext()) {
+            pushed = false;
+            return pushback;
+        }
+
+        throw new NoSuchElementException();
+    }
+
+    public void remove()
+    {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/jaxen/src/java/main/org/jaxen/util/LazyList.java b/jaxen/src/java/main/org/jaxen/util/LazyList.java
new file mode 100644
index 0000000..2cdfe54
--- /dev/null
+++ b/jaxen/src/java/main/org/jaxen/util/LazyList.java
@@ -0,0 +1,349 @@
+package org.jaxen.util;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.ListIterator;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+/*
+ * $Header$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * Copyright (C) 2000-2005 bob mcwhirter & James Strachan.
+ * 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 disclaimer that follows
+ *    these conditions in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. The name "Jaxen" must not be used to endorse or promote products
+ *    derived from this software without prior written permission.  For
+ *    written permission, please contact license@jaxen.org.
+ *
+ * 4. Products derived from this software may not be called "Jaxen", nor
+ *    may "Jaxen" appear in their name, without prior written permission
+ *    from the Jaxen Project Management (pm@jaxen.org).
+ *
+ * In addition, we request (but do not require) that you include in the
+ * end-user documentation provided with the redistribution and/or in the
+ * software itself an acknowledgement equivalent to the following:
+ *     "This product includes software developed by the
+ *      Jaxen Project (http://www.jaxen.org/)."
+ * Alternatively, the acknowledgment may be graphical using the logos
+ * available at http://www.jaxen.org/
+ *
+ * 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 Jaxen AUTHORS OR THE PROJECT
+ * 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 Jaxen Project and was originally
+ * created by bob mcwhirter <bob@werken.com> and
+ * James Strachan <jstrachan@apache.org>.  For more information on the
+ * Jaxen Project, please see <http://www.jaxen.org/>.
+ *
+ * $Id$
+*/
+
+/**
+ * This implementation of List is constructed with an Iterator, and attempts to read as
+ * little as possible.
+ */
+public class LazyList implements List
+{
+    private Iterator source;
+    private List seen;
+    private boolean filled;
+
+    public LazyList(Iterator source)
+    {
+        this.source = source;
+        this.seen = new ArrayList();
+        this.filled = false;
+    }
+
+    public int size()
+    {
+        if (!filled) {
+            fill();
+        }
+        return seen.size();
+    }
+
+    private void fill()
+    {
+        while (source.hasNext()) {
+            seen.add(source.next());
+        }
+        filled = true;
+    }
+
+    public boolean isEmpty()
+    {
+        return seen.isEmpty() && !source.hasNext();
+    }
+
+    public boolean contains(Object item)
+    {
+        return indexOf(item) >= 0;
+    }
+
+    public Iterator iterator()
+    {
+        return listIterator(0);
+    }
+
+    public Object[] toArray()
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.toArray();
+    }
+
+    public Object[] toArray(Object[] objects)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.toArray(objects);
+    }
+
+    public boolean containsAll(Collection collection)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return containsAll(collection);
+    }
+
+    public Object get(int index)
+    {
+        while (index >= seen.size()) {
+            if (!source.hasNext()) {
+                break;
+            }
+            Object nextItem = source.next();
+            seen.add(nextItem);
+        }
+        return seen.get(index);
+    }
+
+
+    public int indexOf(Object item)
+    {
+
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.indexOf(item);
+    }
+
+    public int lastIndexOf(Object item)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.lastIndexOf(item);
+    }
+
+    public ListIterator listIterator()
+    {
+        return listIterator(0);
+    }
+
+    public ListIterator listIterator(final int min)
+    {
+        return new ListIterator() {
+            int index = min - 1;
+
+            public boolean hasNext()
+            {
+                if (index < seen.size() - 1) {
+                    return true;
+                } else {
+                    return source.hasNext();
+                }
+            }
+
+            public Object next()
+            {
+                if (hasNext()) {
+                    index++;
+                    return get(index);
+                }
+                throw new NoSuchElementException();
+            }
+
+            public boolean hasPrevious()
+            {
+                return index > 0;
+            }
+
+            public Object previous()
+            {
+                if (index > 0) {
+                    index--;
+                    return seen.get(index);
+                }
+                throw new IndexOutOfBoundsException();
+            }
+
+            public int nextIndex()
+            {
+                return index - 1;
+            }
+
+            public int previousIndex()
+            {
+                return index + 1;
+            }
+
+            public void remove()
+            {
+                throw new UnsupportedOperationException();
+            }
+
+            public void set(Object node)
+            {
+                if (!filled)
+                {
+                    fill();
+                }
+                seen.set(index, node);
+            }
+
+            public void add(Object node)
+            {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public List subList(int start, int end)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.subList(start, end);
+    }
+
+    public boolean add(Object item)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.add(item);
+    }
+
+    public boolean remove(Object item)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.remove(item);
+    }
+
+    public boolean addAll(Collection collection)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.addAll(collection);
+    }
+
+    public boolean addAll(int index, Collection collection)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.addAll(index, collection);
+    }
+
+    public boolean removeAll(Collection collection)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.removeAll(collection);
+    }
+
+    public boolean retainAll(Collection collection)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.retainAll(collection);
+    }
+
+    public void clear()
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        seen.clear();
+    }
+
+    public Object set(int index, Object item)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.set(index, item);
+    }
+
+    public void add(int index, Object item)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        seen.add(index, item);
+    }
+
+    public Object remove(int index)
+    {
+        if (!filled)
+        {
+            fill();
+        }
+        return seen.remove(index);
+    }
+}
diff --git a/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java b/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java
index f66627f..787e8ec 100644
--- a/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java
+++ b/jaxen/src/java/main/org/jaxen/xom/DocumentNavigator.java
@@ -267,7 +267,7 @@ public class DocumentNavigator extends org.jaxen.DefaultNavigator
         } else if (isNamespace(o)) {
             parent = ((XPathNamespace)o).getElement();
         }
-        return (parent != null ? new SingleObjectIterator(parent) : null);
+        return (parent != null ? new SingleObjectIterator(parent) : JaxenConstants.EMPTY_ITERATOR);
     }
 
     public Object getParentNode(Object o)  {

