JENA-1809: Improvements to Iter
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/optimizer/reorder/ReorderTransformationSubstitution.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/optimizer/reorder/ReorderTransformationSubstitution.java
index decf1cd..d05f361 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/optimizer/reorder/ReorderTransformationSubstitution.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/optimizer/reorder/ReorderTransformationSubstitution.java
@@ -22,7 +22,8 @@
 
 import java.util.ArrayList ;
 import java.util.List ;
-import org.apache.jena.atlas.iterator.AccString ;
+import java.util.stream.Collectors;
+
 import org.apache.jena.atlas.iterator.Iter ;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.graph.Node ;
@@ -74,13 +75,8 @@
         return components ;
     }
 
-    private AccString<PatternTriple> formatter() { 
-        return new AccString<PatternTriple>() { 
-            @Override
-            protected String toString(PatternTriple pt) {
-                return "(" + printAbbrev(pt.toString()) + ")" ;
-            }
-        } ;
+    private String formatted(List<PatternTriple> components) {
+        return components.stream().map(c->"(" + printAbbrev(c.toString()) + ")").collect(Collectors.joining(" "));
     }
     
     protected ReorderProc reorder(List<Triple> triples, List<PatternTriple> components) {
@@ -89,7 +85,7 @@
         int indexes[] = new int[N] ;
 
         if ( DEBUG )
-            log.debug("Reorder: " + Iter.asString(components, formatter())) ;
+            log.debug("Reorder: " + formatted(components));
 
         int idx = 0 ;
         for ( ; idx < numReorder ; idx++ ) {
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java
index 91ba3a8..3a5ee08 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java
@@ -57,7 +57,7 @@
         
         try {
             UpdateSink sink = uProc.getUpdateSink();
-            Iter.sendToSink(request, sink);     // Will call close on sink if there are no exceptions
+            Iter.sendToSink(request.iterator(), sink);     // Will call close on sink if there are no exceptions
         } finally {
             uProc.finishRequest() ;
         }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
index 7672f71..cf38e50 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
@@ -176,7 +176,7 @@
     @Override
     public void visit(UpdateDataInsert update)
     {
-        Iter.sendToSink(update.getQuads(), createInsertDataSink());  // Iter.sendToSink() will call close() on the sink
+        Iter.sendToSink(update.getQuads().iterator(), createInsertDataSink());  // Iter.sendToSink() will call close() on the sink
     }
     
     @Override
@@ -190,7 +190,7 @@
     @Override
     public void visit(UpdateDataDelete update)
     {
-        Iter.sendToSink(update.getQuads(), createDeleteDataSink()); // Iter.sendToSink() will call close() on the sink
+        Iter.sendToSink(update.getQuads().iterator(), createDeleteDataSink()); // Iter.sendToSink() will call close() on the sink
     }
 
     // Prettier later.
@@ -205,7 +205,7 @@
         
         SinkQuadBracedOutput sink = new SinkQuadBracedOutput(out, sCxt);
         sink.open();
-        Iter.sendToSink(quads, sink);
+        Iter.sendToSink(quads.iterator(), sink);
     }
     
     protected void output(Node node)
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java
index 37671b8..df33ea4 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java
@@ -37,7 +37,10 @@
         
     @Override
     public String toString() {
-        return Iter.asString(listElementsMap.keySet(), ", ") + "\n" + "{"+ Iter.asString(triplesInLists.iterator(), "\n")+"}";
-            
+        
+        return Iter.asString(listElementsMap.keySet().iterator(), ", ")
+            + "\n" + "{"+
+            Iter.asString(triplesInLists.iterator(), "\n")
+            +"}";
     }
 }
\ No newline at end of file
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/StringUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/StringUtils.java
index 154c1fc..dc40a3e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/StringUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/StringUtils.java
@@ -72,7 +72,7 @@
     /** Abbreviate, crudely, URI in strings, leaving only their last component. */ 
     public static <T> String printAbbrevList(List<T> objs)
     {
-        String x = Iter.asString(objs, "\n") ;
+        String x = Iter.asString(objs.iterator(), "\n") ;
         return printAbbrev(x) ;
     }
 }
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryBasic.java b/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryBasic.java
index 7401f70..25d5fb2 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryBasic.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryBasic.java
@@ -19,8 +19,8 @@
 package org.apache.jena.sparql.core.mem;
 
 import static java.lang.System.err ;
+import static org.apache.jena.atlas.iterator.Iter.anyMatch ;
 import static org.apache.jena.atlas.iterator.Iter.iter ;
-import static org.apache.jena.atlas.iterator.Iter.some ;
 import static org.apache.jena.graph.Node.ANY ;
 import static org.apache.jena.graph.NodeFactory.createBlankNode ;
 import static org.apache.jena.graph.NodeFactory.createURI ;
@@ -58,7 +58,7 @@
         final Triple triple = parseTriple("(:s :p :o)");
 		dsg.getDefaultGraph().add(triple);
         final Iterator<Triple> iter = dsg.getDefaultGraph().find(null, p, null) ;
-        assertTrue(some(iter, triple::equals));
+        assertTrue(anyMatch(iter, triple::equals));
 
 
         final Node p1 = parseNode(":p1") ;
@@ -67,7 +67,7 @@
 
         final Iterator<Quad> iter2 = dsg.find(null, null, p1, null) ;
 
-        assertTrue(some(iter2, quad::equals));
+        assertTrue(anyMatch(iter2, quad::equals));
         Iter.print(err,iter2);
 	}
 
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java
deleted file mode 100644
index 7df3486..0000000
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.atlas.iterator;
-
-public class AccString<T> implements Accumulate<T, String>
-{
-    StringBuilder buffer = null ;
-    private String sep ;
-    private boolean first = true ;
-
-    // Fresh StringBuilder
-    public AccString(String sep) { this.sep = sep ; }
-    public AccString() { this(" ") ; }
-
-    @Override
-    public void accumulate(T item)
-    { 
-        if ( ! first )
-            buffer.append(sep) ;
-        if ( item != null )
-            buffer.append(toString(item)) ;
-        else
-            buffer.append("<null>") ;
-        first = false ;
-    }
-
-    /** Make into a string */
-    protected String toString(T item)
-    {
-        return item.toString() ; 
-    }
-    
-    @Override
-    public String get()
-    {
-        return buffer.toString() ;
-    }
-
-    @Override
-    public void start()
-    { 
-        // Resets on each use.
-        buffer = new StringBuilder() ; 
-        first = true ;
-    }
-    
-    @Override
-    public void finish() {}
-
-}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.java
deleted file mode 100644
index 2eef4fa..0000000
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.atlas.iterator;
-
-public interface Accumulate <T, R>
-{
-    public void start() ;
-    public void accumulate(T item) ;
-    public void finish();
-    public R get() ;
-}
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
index 17da134..97661fa 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
@@ -20,9 +20,9 @@
 
 import java.io.PrintStream ;
 import java.util.* ;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.function.*;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
 import java.util.stream.Stream ;
 import java.util.stream.StreamSupport ;
 
@@ -53,6 +53,12 @@
  * @param <T> the type of element over which an instance of {@code Iter} iterates,
  */
 public class Iter<T> implements Iterator<T> {
+    
+    /** Shorter form of "forEachRemaining" */
+    public static <T> void forEach(Iterator<T> iter, Consumer<T> action) {
+        iter.forEachRemaining(action);
+    }
+    
     // IteratorSlotted needed? IteratorPeek
     //   IteratorSlotted.inspect
     
@@ -82,21 +88,12 @@
     
     /** Collect an iterator into a set. */
     public static <T> Set<T> toSet(Iterator<? extends T> stream) {
-        Set<T> acc = new HashSet<>() ;
-        collect(acc, stream) ;
-        return acc ;
+        return collect(stream, Collectors.toSet());
     }
     
     /** Collect an iterator into a list. */
     public static <T> List<T> toList(Iterator<? extends T> stream) {
-        List<T> acc = new ArrayList<>() ;
-        collect(acc, stream) ;
-        return acc ;
-    }
-
-    /** Collect an iterator. */
-    private static <T> void collect(Collection<T> acc, Iterator<? extends T> stream) {
-        stream.forEachRemaining((x)->acc.add(x)) ;
+        return collect(stream, Collectors.toList());
     }
 
     /**
@@ -108,61 +105,95 @@
         return x.iterator() ;
     }
     
+    // Note fold-left and fold-right
+    // http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29
+
+    // This reduce is a kind of fold-left (take first element, apply to rest of list)
+    // and can deal with lists of zero elements.
+    
     // -- Operations on iterators.
 
-    public interface Folder<X, Y> {
-        Y eval(Y acc, X arg) ;
-    }
+    @FunctionalInterface
+    public interface Folder<X, Y> extends BiFunction<Y,X,Y> {}
 
-    public static <T, R> R foldLeft(Iterable<? extends T> stream, Folder<T, R> function, R value) {
-        return foldLeft(stream.iterator(), function, value) ;
-    }
-
-    public static <T, R> R foldLeft(Iterator<? extends T> stream, Folder<T, R> function, R value) {
+    public static <T, R> R foldLeft(Iterator<? extends T> stream, R value, Folder<T, R> function) {
         // Tail recursion, unwound
         for (; stream.hasNext();) {
             T item = stream.next() ;
-            value = function.eval(value, item) ;
+            value = function.apply(value, item) ;
         }
         return value ;
     }
 
-    public static <T, R> R foldRight(Iterable<? extends T> stream, Folder<T, R> function, R value) {
-        return foldRight(stream.iterator(), function, value) ;
-    }
-
-    public static <T, R> R foldRight(Iterator<? extends T> stream, Folder<T, R> function, R value) {
+    public static <T, R> R foldRight(Iterator<? extends T> stream, R value, Folder<T, R> function) {
         // Recursive.
         if ( !stream.hasNext() )
             return value ;
         T item = stream.next() ;
-        return function.eval(foldRight(stream, function, value), item) ;
+        return function.apply(foldRight(stream, value, function), item) ;
     }
 
-    // Note fold-left and fold-right
-    // http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29
-
-    // This reduce is fold-left (take first element, apply to rest of list)
-    // which copes with infinite lists.
-    // Fold-left starts by combining the first element, then moves on.
-
-    /** Reduce by aggregator.
-     * This reduce is fold-left (take first element, apply to rest of list)
-     */
-    public static <T, R> R reduce(Iterable<? extends T> stream, Accumulate<T, R> aggregator) {
-        return reduce(stream.iterator(), aggregator) ;
+    public static <T> Optional<T> reduce(Iterator<T> iter, BinaryOperator<T> accumulator) {
+        T r = reduce(iter, null, accumulator);
+        return Optional.ofNullable(r);
     }
-    /** Reduce by aggregator.
-     * This reduce is fold-left (take first element, apply to rest of list)
-     */
-    public static <T, R> R reduce(Iterator<? extends T> stream, Accumulate<T, R> aggregator) {
-        aggregator.start() ;
-        for (; stream.hasNext();) {
-            T item = stream.next() ;
-            aggregator.accumulate(item) ;
+
+    public static <T> T reduce(Iterator<T> iter, T identity, BinaryOperator<T> accumulator) {
+        T result = identity;
+        while(iter.hasNext()) {
+            T elt = iter.next();
+            result = (result == null) ? elt : accumulator.apply(result, elt);
         }
-        aggregator.finish() ;
-        return aggregator.get() ;
+        return result;
+    }
+
+    // ---- min and max
+    public static <T> Optional<T> min(Iterator<T> iter, Comparator<T> comparator) {
+        T x = null;
+        while(iter.hasNext()) {
+            T elt = iter.next();
+            if ( x == null )
+                x = elt;
+            else {
+                int cmp = comparator.compare(x, elt);
+                if ( cmp > 0 )
+                    x = elt;
+            }
+        }
+        return Optional.ofNullable(x);
+    }
+
+    public static <T> Optional<T> max(Iterator<T> iter, Comparator<T> comparator) {
+        // Or min(iter, comparator.reversed())
+        T x = null;
+        while(iter.hasNext()) {
+            T elt = iter.next();
+            if ( x == null )
+                x = elt;
+            else {
+                int cmp = comparator.compare(x, elt);
+                if ( cmp < 0 )
+                    x = elt;
+            }
+        }
+        return Optional.ofNullable(x);
+    }
+
+    // ---- collect
+    /** See {@link Stream#collect(Supplier, BiConsumer, BiConsumer)}, except without the {@code BiConsumer<R, R> combiner} */
+    public static <T,R> R collect(Iterator<T> iter, Supplier<R> supplier, BiConsumer<R, ? super T> accumulator) {
+        R result = supplier.get();
+        while(iter.hasNext()) {
+            T elt = iter.next();
+            accumulator.accept(result, elt);
+        }
+        return result;
+    }
+
+    /** See {@link Stream#collect(Collector)} */
+    public static <T, R, A> R collect(Iterator<T> iter, Collector<? super T, A, R> collector) {
+        A a = collect(iter, collector.supplier(), collector.accumulator());
+        return collector.finisher().apply(a);
     }
 
     /** Act on elements of an iterator.
@@ -230,29 +261,80 @@
     /**
      * Return true if every element of stream passes the filter (reads the
      * stream until the first element not passing the filter)
+     * @deprecated Use {@link #allMatch}
      */
-    public static <T> boolean every(Iterator<? extends T> stream, Predicate<T> filter) {
-        while ( stream.hasNext() ) {
-            T item = stream.next() ;
-            if ( !filter.test(item) )
+    @Deprecated
+    public static <T> boolean every(Iterator<? extends T> iter, Predicate<T> predicate) {
+        return Iter.allMatch(iter, predicate);
+    }
+
+    /**
+     * Return true if every element of stream passes the filter (reads the
+     * stream until the first element not passing the filter)
+     */
+    public static <T> boolean allMatch(Iterator<T> iter, Predicate<? super T> predicate) {
+        while ( iter.hasNext() ) {
+            T item = iter.next() ;
+            if ( !predicate.test(item) )
                 return false ;
         }
         return true ;
     }
+    
+    /**
+     * Return true if one or more elements of stream passes the filter (reads
+     * the stream to first element passing the filter)
+     * @deprecated Use {@link #anyMatch}
+     */
+    @Deprecated
+    public static <T> boolean some(Iterator<T> iter, Predicate<? super T> predicate) {
+        return Iter.anyMatch(iter, predicate);
+    }
 
     /**
      * Return true if one or more elements of stream passes the filter (reads
      * the stream to first element passing the filter)
      */
-    public static <T> boolean some(Iterator<? extends T> stream, Predicate<T> filter) {
-        while ( stream.hasNext() ) {
-            T item = stream.next() ;
-            if ( filter.test(item) )
+    public static <T> boolean anyMatch(Iterator<T> iter, Predicate<? super T> predicate) {
+        while ( iter.hasNext() ) {
+            T item = iter.next() ;
+            if ( predicate.test(item) )
                 return true ;
         }
         return false ;
     }
 
+    /**
+     * Return true if none of the elements of the iterator passes the predicate test  reads
+     * the stream to first element passing the filter)
+     */
+    public static <T> boolean noneMatch(Iterator<T> iter, Predicate<? super T> predicate) {
+        return ! anyMatch(iter, predicate);
+    }
+
+    /**
+     * Return an Optional with the first element of an iterator that matches the predicate.
+     * Return {@code Optional.empty} if none match.
+     * Reads the iterator until the first match.
+     */
+    public static <T> Optional<T> findFirst(Iterator<T> iter, Predicate<? super T> predicate) {
+        while ( iter.hasNext() ) {
+            T item = iter.next() ;
+            if ( predicate.test(item) )
+                return Optional.of(item);
+        }
+        return Optional.empty() ;
+    }
+
+    /**
+     * Return an Optional with an element of an iterator that matches the predicate.
+     * Return {@code Optional.empty} if none match.
+     * The element returned is not specified by the API contract. 
+     */
+    public static <T> Optional<T> findAny(Iterator<T> iter, Predicate<? super T> predicate) {
+        return findFirst(iter, predicate);
+    }
+
     // ---- Map
 
     /** Apply a function to every element of an iterator, transforming it
@@ -525,35 +607,16 @@
         count(iterator) ;
     }
 
-    // ---- String related helpers
-    // Java8 has StringJoin
-
-    public static <T> String asString(Iterable<T> stream) {
-        return asString(stream, new AccString<T>()) ;
-    }
-
-    public static <T> String asString(Iterator<T> stream) {
-        return asString(stream, new AccString<T>()) ;
-    }
-
-    public static <T> String asString(Iterable<T> stream, String sep) {
-        return asString(stream, new AccString<T>(sep)) ;
-    }
-
+    /** Create a string from an iterator, using the separator. Note: this consumes the iterator. */
     public static <T> String asString(Iterator<T> stream, String sep) {
-        return asString(stream, new AccString<T>(sep)) ;
+        return Iter.iter(stream).map(x->x.toString()).collect(Collectors.joining(sep));
     }
-
-    public static <T> String asString(Iterable<T> stream, AccString<T> formatter) {
-        return asString(stream.iterator(), formatter) ;
+    
+    /** Create a string from an iterator, using the separator, prefix and suffix. Note: this consumes the iterator. */
+    public static <T> String asString(Iterator<T> stream, CharSequence sep, CharSequence prefix, CharSequence suffix) {
+        return Iter.iter(stream).map(x->x.toString()).collect(Collectors.joining(sep, prefix, suffix));
     }
-
-    public static <T> String asString(Iterator<T> stream, AccString<T> formatter) {
-        return reduce(stream, formatter) ;
-    }
-
-    // ----
-
+    
     public static <T> void close(Iterator<T> iter) {
         if ( iter instanceof Closeable )
             ((Closeable)iter).close() ;
@@ -618,6 +681,7 @@
     }
 
     /** Send the elements of the iterable to a sink */
+    @Deprecated
     public static <T> void sendToSink(Iterable<T> stream, Sink<T> sink) {
         sendToSink(stream.iterator(), sink) ;
     }
@@ -762,6 +826,11 @@
     private Iter(Iterator<T> iterator) {
         this.iterator = iterator ;
     }
+    
+    /** Apply the Consumer to each element of the iterator */
+    public void forEach(Consumer<T> action) {
+        iterator.forEachRemaining(action);
+    }
 
     /** Consume the {@code Iter} and produce a {@code Set} */
     public Set<T> toSet() {
@@ -776,7 +845,7 @@
     public void sendToSink(Sink<T> sink) {
         sendToSink(iterator, sink) ;
     }
-
+    
     public T first() {
         return first(iterator) ;
     }
@@ -811,8 +880,8 @@
     }
 
     /** Return true if every element satisfies a predicate */ 
-    public boolean every(Predicate<T> predciate) {
-        return every(iterator, predciate) ;
+    public boolean every(Predicate<T> predicate) {
+        return every(iterator, predicate) ;
     }
 
     /** Return true if some element satisfies a predicate */ 
@@ -820,6 +889,26 @@
         return some(iterator, filter) ;
     }
 
+    public boolean allMatch(Predicate<? super T> predicate) {
+        return allMatch(iterator, predicate);
+    }
+
+    public boolean anyMatch(Predicate<? super T> predicate) {
+        return anyMatch(iterator, predicate);
+    }
+
+    public boolean noneMatch(Predicate<? super T> predicate) {
+        return noneMatch(iterator, predicate);
+    }
+
+    public Optional<T> findFirst(Predicate<? super T> predicate) {
+        return findFirst(iterator, predicate);
+    }
+
+    public Optional<T> findAny(Predicate<? super T> predicate) {
+        return findAny(iterator, predicate);
+    }
+
     /** Remove nulls */
     public Iter<T> removeNulls() {
         return iter(removeNulls(this)) ;
@@ -842,13 +931,43 @@
         return iter(operate(iterator, action)) ;
     }
 
-    /** Reduce by aggregator.
+    public <R> R foldLeft(R initial, Folder<T, R> accumulator) {
+        return foldLeft(iterator, initial, accumulator) ;
+    }
+    
+    public <R> R foldRight(R initial, Folder<T, R> accumulator) {
+        return foldRight(iterator, initial, accumulator) ;
+    }
+    
+    /** Reduce.
      * This reduce is fold-left (take first element, apply to rest of list)
      */
-    public <R> R reduce(Accumulate<T, R> aggregator) {
-        return reduce(iterator, aggregator) ;
+    public Optional<T> reduce(BinaryOperator<T> accumulator) {
+        return reduce(iterator, accumulator) ;
     }
 
+    public T reduce(T identity, BinaryOperator<T> accumulator) {
+        return Iter.reduce(iterator, identity, accumulator);
+    }
+
+    public Optional<T> min(Comparator<T> comparator) {
+        return min(iterator, comparator);
+    }
+
+    public Optional<T> max(Comparator<T> comparator) {
+        return max(iterator, comparator);
+    }
+    
+    /** See {@link Stream#collect(Supplier, BiConsumer, BiConsumer)}, except without the {@code BiConsumer<R, R> combiner} */
+    public <R> R collect(Supplier<R> supplier, BiConsumer<R, T> accumulator/*, BiConsumer<R, R> combiner*/) {
+        return Iter.collect(iterator, supplier, accumulator);
+    }
+
+    /** See {@link Stream#collect(Collector)} */
+    public <R, A> R collect(Collector<? super T, A, R> collector) {
+        return collect(iterator, collector);
+    }
+    
     /** Apply an action to every element of an iterator */ 
     public void apply(Consumer<T> action) {
         apply(iterator, action) ;
@@ -925,15 +1044,8 @@
         return action.getCount() ;
     }
 
-    public String asString() {
-        return asString(iterator) ;
-    }
-
-    public String asString(String sep) {
-        return asString(iterator, sep) ;
-    }
-
-    /** Return an {:@code Iter} that will see each element of the underlying iterator only once.
+    /**
+     * Return an {@code Iter} that will see each element of the underlying iterator only once.
      * Note that this need working memory to remember the elements already seen.
      */
     public Iter<T> distinct() {
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/StrUtils.java b/jena-base/src/main/java/org/apache/jena/atlas/lib/StrUtils.java
index 796a7ca..0b1989f 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/lib/StrUtils.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/StrUtils.java
@@ -26,6 +26,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /** Some functions that act on strings */
 public class StrUtils //extends StringUtils
@@ -42,28 +43,11 @@
         return String.join("\n", args);
     }
 
-    /** 
-     * Concatentate strings, using a separator
-     * 
-     * This function will be removed - do not use.
-     * 
-     * @deprecated Prefer String.join(sep, args)
-     */
-    @Deprecated
-    public static String strjoin(String sep, String... args) {
-        return String.join(sep, args);
-    }
-
     /**
-     * Concatentate strings, using a separator
-     * 
-     * This function will be removed - do not use.
-     * 
-     * @deprecated Prefer String.join(sep, args)
+     * Concatentate stringified objects, using a separator.
      */
-    @Deprecated
-    public static String strjoin(String sep, List<String> args) {
-        return String.join(sep, args);
+    public static <X> String strjoin(List<X> args, String sep) {
+        return args.stream().map(obj->obj.toString()).collect(Collectors.joining(sep));
     }
 
     public static final int CMP_GREATER  = +1 ;
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java b/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
index c803240..b00fcff 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
@@ -23,18 +23,20 @@
 import static org.junit.Assert.assertTrue ;
 
 import java.util.* ;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 import org.junit.Test ;
 
 public class TestIter
 {
-    List<String> data0 = new ArrayList<>() ;
-    List<String> data1 = Arrays.asList("a") ;
-    List<String> data2 = Arrays.asList("x","y","z") ;
-    List<String> data3 = Arrays.asList(null, "x", null, null, null, "y", "z", null);
+    private List<String> data0 = new ArrayList<>() ;
+    private List<String> data1 = Arrays.asList("a") ;
+    private List<String> data2 = Arrays.asList("x","y","z") ;
+    private List<String> data3 = Arrays.asList(null, "x", null, null, null, "y", "z", null);
  
     @Test
     public void append_1() {
@@ -54,42 +56,37 @@
         test(iter, "a", "x", "y", "z");
     }
 
+    private static List<String> mutableList(String...strings) {
+        List<String> list = new ArrayList<>();
+        for ( String s : strings )
+            list.add(s);
+        return list;
+    }
+    
     @Test
     public void append_4() {
-        List<String> L = new ArrayList<>(3);
-        L.add("a");
-        L.add("b");
-        L.add("c");
-        List<String> R = new ArrayList<>(3);
-        R.add("d");
-        R.add("e");
-        R.add("f");
+        List<String> L = mutableList("a", "b", "c");
+        List<String> R = mutableList("d", "e", "f");
 
         Iterator<String> LR = Iter.append(L.iterator(), R.iterator());
 
         while (LR.hasNext()) {
             String s = LR.next();
-
             if ( "c".equals(s) ) {
                 LR.hasNext();  // test for JENA-60
                 LR.remove();
             }
         }
 
-        assertEquals("ab", Iter.asString(L, ""));
-        assertEquals("def", Iter.asString(R, ""));
+        assertEquals(2, L.size());
+        assertEquals(Arrays.asList("a", "b"), L);
+        assertEquals(Arrays.asList("d", "e", "f"), R);
     }
 
     @Test
     public void append_5() {
-        List<String> L = new ArrayList<>(3);
-        L.add("a");
-        L.add("b");
-        L.add("c");
-        List<String> R = new ArrayList<>(3);
-        R.add("d");
-        R.add("e");
-        R.add("f");
+        List<String> L = mutableList("a", "b", "c");
+        List<String> R = mutableList("d", "e", "f");
 
         Iterator<String> LR = Iter.append(L.iterator(), R.iterator());
 
@@ -102,20 +99,14 @@
             }
         }
 
-        assertEquals("abc", Iter.asString(L, ""));
-        assertEquals("ef", Iter.asString(R, ""));
+        assertEquals(3, L.size());
+        assertEquals(Arrays.asList("e", "f"), R);
     }
 
     @Test
     public void append_6() {
-        List<String> L = new ArrayList<>(3);
-        L.add("a");
-        L.add("b");
-        L.add("c");
-        List<String> R = new ArrayList<>(3);
-        R.add("d");
-        R.add("e");
-        R.add("f");
+        List<String> L = mutableList("a", "b", "c");
+        List<String> R = mutableList("d", "e", "f");
 
         Iterator<String> LR = Iter.append(L.iterator(), R.iterator());
 
@@ -124,32 +115,9 @@
         }
         LR.remove();
 
-        assertEquals("abc", Iter.asString(L, ""));
-        assertEquals("de", Iter.asString(R, ""));
-    }
-
-    @Test
-    public void asString_1() {
-        String x = Iter.asString(data0, "");
-        assertEquals("", x);
-    }
-
-    @Test
-    public void asString_2() {
-        String x = Iter.asString(data1, "");
-        assertEquals("a", x);
-    }
-
-    @Test
-    public void asString_3() {
-        String x = Iter.asString(data1, "/");
-        assertEquals("a", x);
-    }
-
-    @Test
-    public void asString_4() {
-        String x = Iter.asString(data2, "/");
-        assertEquals("x/y/z", x);
+        assertEquals(3, L.size());
+        assertEquals(Arrays.asList("a", "b", "c"), L);
+        assertEquals(Arrays.asList("d", "e"), R);
     }
 
     private static void test(Iterator<? > iter, Object...items) {
@@ -165,28 +133,28 @@
     @Test
     public void fold_01() {
         String[] x = {"a", "b", "c"};
-        String z = Iter.foldLeft(Arrays.asList(x), f1, "X");
+        String z = Iter.foldLeft(Arrays.asList(x).iterator(), "X", f1);
         assertEquals("Xabc", z);
     }
 
     @Test
     public void fold_02() {
         String[] x = {"a", "b", "c"};
-        String z = Iter.foldRight(Arrays.asList(x), f1, "X");
+        String z = Iter.foldRight(Arrays.asList(x).iterator(), "X", f1);
         assertEquals("Xcba", z);
     }
 
     @Test
     public void fold_03() {
         String[] x = {};
-        String z = Iter.foldLeft(Arrays.asList(x), f1, "X");
+        String z = Iter.foldLeft(Arrays.asList(x).iterator(), "X", f1);
         assertEquals("X", z);
     }
 
     @Test
     public void fold_04() {
         String[] x = {};
-        String z = Iter.foldRight(Arrays.asList(x), f1, "X");
+        String z = Iter.foldRight(Arrays.asList(x).iterator(), "X", f1);
         assertEquals("X", z);
     }
 
@@ -355,6 +323,139 @@
         int x = Iter.step(iter, 6);
         assertEquals(5, x);
     }
+    
+    @SafeVarargs
+    private static <X> Iterator<X> data(X ... items) {
+        List<X> a = new ArrayList<>(items.length);
+        for (X x : items )
+            a.add(x);
+        return a.iterator();
+    }
+
+    @Test public void anyMatch1() {
+        boolean b = Iter.anyMatch(data("2"), x->x.equals("2"));
+        assertTrue(b);
+    }
+
+    @Test public void anyMatch2() {
+        boolean b = Iter.anyMatch(data("2","3"), x->x.equals("2"));
+        assertTrue(b);
+    }
+
+    @Test public void anyMatch3() {
+        boolean b = Iter.anyMatch(data("2","3"), x->x.equals("1"));
+        assertFalse(b);
+    }
+
+    @Test public void allMatch1() {
+        boolean b = Iter.allMatch(data("2", "2"), x->x.equals("2"));
+        assertTrue(b);
+    }
+
+    @Test public void allMatch2() {
+        boolean b = Iter.allMatch(data("1", "2"), x->x.equals("2"));
+        assertFalse(b);
+    }
+
+
+    @Test public void noneMatch1() {
+        boolean b = Iter.noneMatch(data("1", "2", "3"), x->x.equals("A"));
+        assertTrue(b);
+    }
+
+    @Test public void noneMatch2() {
+        boolean b = Iter.noneMatch(data("A", "2", "3"), x->x.equals("A"));
+        assertFalse(b);
+    }
+
+    @Test public void findFirst1() {
+        Optional<String> r = Iter.findFirst(data("A", "2", "3"), x->x.equals("A"));
+        assertTrue(r.isPresent());
+        assertEquals("A", r.get());
+    }
+
+    @Test public void findFirst2() {
+        Optional<String> r = Iter.findFirst(data("A", "2", "3"), x->x.equals("Z"));
+        assertFalse(r.isPresent());
+    }
+
+    @Test public void findAny1() {
+        Optional<String> r = Iter.findAny(data("A", "2", "3"), x->x.equals("A"));
+        assertTrue(r.isPresent());
+        assertEquals("A", r.get());
+    }
+
+    @Test public void reduce1() {
+        Optional<String> r = Iter.reduce(data("A", "2", "3"), String::concat);
+        assertEquals(Optional.of("A23"), r);
+    }
+
+    @Test public void reduce2() {
+        Optional<String> r = Iter.reduce(data("A"), String::concat);
+        assertEquals(Optional.of("A"), r);
+    }
+
+    @Test public void reduce3() {
+        Optional<String> r = Iter.reduce(data(), String::concat);
+        assertFalse(r.isPresent());
+    }
+
+    @Test public void min1() {
+        Optional<String> x = Iter.min(data(), String::compareTo);
+        assertFalse(x.isPresent());
+    }
+
+    @Test public void min2() {
+        Optional<String> x = Iter.min(data("2"), String::compareTo);
+        assertTrue(x.isPresent());
+        assertEquals("2", x.get());
+    }
+
+    @Test public void min3() {
+        Optional<String> x = Iter.min(data("1", "2", "3"), String::compareTo);
+        assertTrue(x.isPresent());
+        assertEquals("1", x.get());
+    }
+
+    @Test public void min4() {
+        Optional<String> x = Iter.min(data("3", "1", "2"), String::compareTo);
+        assertTrue(x.isPresent());
+        assertEquals("1", x.get());
+    }
+
+    @Test public void max1() {
+        Optional<String> x = Iter.max(data(), String::compareTo);
+        assertFalse(x.isPresent());
+    }
+
+    @Test public void max2() {
+        Optional<String> x = Iter.max(data("2"), String::compareTo);
+        assertTrue(x.isPresent());
+        assertEquals("2", x.get());
+    }
+
+    @Test public void max3() {
+        Optional<String> x = Iter.max(data("1", "2", "3"), String::compareTo);
+        assertTrue(x.isPresent());
+        assertEquals("3", x.get());
+    }
+
+    @Test public void max4() {
+        Optional<String> x = Iter.max(data("3", "1", "2"), String::compareTo);
+        assertTrue(x.isPresent());
+        assertEquals("3", x.get());
+    }
+
+    @Test public void collect3() {
+        List<String> x = Iter.collect(data("A", "B", "C"), Collectors.toList());
+        assertEquals(3, x.size());
+        assertEquals(Arrays.asList("A", "B", "C"), x);
+    }
+
+    @Test public void collect1() {
+        List<String> x = Iter.collect(data("A", "B", "C"), ArrayList::new, ArrayList::add);
+        assertEquals(Arrays.asList("A", "B", "C"), x);
+    }
 
     @Test
     public void take_01() {
@@ -366,6 +467,22 @@
     }
 
     @Test
+    public void forEach_1() {
+        List<String> data = Arrays.asList("1", "A", "B", "CC");
+        AtomicInteger counter = new AtomicInteger(0);
+        Iter.forEach(data.iterator(), x->counter.incrementAndGet());
+        assertEquals(4, counter.get());
+    }
+    
+    @Test
+    public void forEach_2() {
+        List<String> data = Collections.emptyList();
+        AtomicInteger counter = new AtomicInteger(0);
+        Iter.forEach(data.iterator(), x->counter.incrementAndGet());
+        assertEquals(0, counter.get());
+    }
+    
+    @Test
     public void take_02() {
         List<String> data = Arrays.asList("1", "A", "B", "CC");
         List<String> data2 = Iter.take(data.iterator(), 0);
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java
index 7a73c8f..d3c2460 100644
--- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageMatchTuple.java
@@ -25,6 +25,7 @@
 
 import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.atlas.iterator.RepeatApplyIterator;
+import org.apache.jena.atlas.lib.StrUtils;
 import org.apache.jena.atlas.lib.tuple.Tuple;
 import org.apache.jena.atlas.lib.tuple.TupleFactory;
 import org.apache.jena.graph.Node;
@@ -151,7 +152,7 @@
         else
         {
             List<Tuple<NodeId>> r = Iter.toList(iter);
-            String str = Iter.asString(r, "\n");
+            String str = StrUtils.strjoin(r, "\n");
             System.err.println(str);
             // Reset iter
             iter = Iter.iter(r);
diff --git a/jena-sdb/src/main/java/org/apache/jena/sdb/compiler/SqlStageList.java b/jena-sdb/src/main/java/org/apache/jena/sdb/compiler/SqlStageList.java
index d033720..6ca5299 100644
--- a/jena-sdb/src/main/java/org/apache/jena/sdb/compiler/SqlStageList.java
+++ b/jena-sdb/src/main/java/org/apache/jena/sdb/compiler/SqlStageList.java
@@ -20,7 +20,7 @@
 
 import java.util.ArrayList;
 
-import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.StrUtils;
 import org.apache.jena.sdb.core.SDBRequest ;
 import org.apache.jena.sdb.core.sqlnode.SqlNode ;
 
@@ -49,7 +49,7 @@
         if ( isEmpty() )
             str = str + " (empty)" ;
         else
-            str = str + " "+ Iter.asString(this, " // " ) ;
+            str = str + " "+ StrUtils.strjoin(this, " // " ) ;
         //str = str + "\n" ;
         return str ;
     }
diff --git a/jena-sdb/src/main/java/org/apache/jena/sdb/store/FeatureSet.java b/jena-sdb/src/main/java/org/apache/jena/sdb/store/FeatureSet.java
index 66316d8..a1e273a 100644
--- a/jena-sdb/src/main/java/org/apache/jena/sdb/store/FeatureSet.java
+++ b/jena-sdb/src/main/java/org/apache/jena/sdb/store/FeatureSet.java
@@ -21,10 +21,10 @@
 import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;
+import java.util.stream.Collectors;
 
 import org.apache.jena.atlas.io.IndentedWriter ;
 import org.apache.jena.atlas.io.PrintableBase ;
-import org.apache.jena.atlas.iterator.Iter ;
 
 /** A set of features (order retained */
 
@@ -61,6 +61,7 @@
     @Override
     public void output(IndentedWriter out)
     {
-        out.print(Iter.asString(features)) ;
+        String x = features.stream().map(f->f.toString()).collect(Collectors.joining(" ")) ;
+        out.print(x) ;
     }
 }
diff --git a/jena-tdb/src/main/java/org/apache/jena/tdb/lib/Lib2.java b/jena-tdb/src/main/java/org/apache/jena/tdb/lib/Lib2.java
deleted file mode 100644
index c92ef09..0000000
--- a/jena-tdb/src/main/java/org/apache/jena/tdb/lib/Lib2.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.tdb.lib;
-
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.jena.atlas.iterator.Iter ;
-
-
-public class Lib2
-{
-    private static Pattern p = Pattern.compile("http:[^ \n]*[#/]([^/ \n]*)") ;
-    /** Abbreviate, crudely, URI in strings, leaving only their last component. */ 
-    public static String printAbbrev(Object obj)
-    {
-        if ( obj==null )
-            return "<null>" ;
-        String x = obj.toString() ;
-        return p.matcher(x).replaceAll("::$1") ;
-    }
-    
-    /** Abbreviate, crudely, URI in strings, leaving only their last component. */ 
-    public static <T> String printAbbrevList(List<T> objs)
-    {
-        String x = Iter.asString(objs, "\n") ;
-        return printAbbrev(x) ;
-    }
-}
diff --git a/jena-tdb/src/main/java/org/apache/jena/tdb/solver/SolverLib.java b/jena-tdb/src/main/java/org/apache/jena/tdb/solver/SolverLib.java
index ff54d73..120eb5e 100644
--- a/jena-tdb/src/main/java/org/apache/jena/tdb/solver/SolverLib.java
+++ b/jena-tdb/src/main/java/org/apache/jena/tdb/solver/SolverLib.java
@@ -19,7 +19,6 @@
 package org.apache.jena.tdb.solver;
 
 import static org.apache.jena.atlas.lib.tuple.TupleFactory.tuple ;
-import static org.apache.jena.tdb.lib.Lib2.printAbbrev ;
 
 import java.util.* ;
 import java.util.function.Function;
@@ -314,14 +313,6 @@
         return new QueryIterTDB(iterBinding, killList, input, execCxt) ;
     }
     
-    /** Turn a BasicPattern into an abbreviated string for debugging */  
-    public static String strPattern(BasicPattern pattern)
-    {
-        List<Triple> triples = pattern.getList() ;
-        String x = Iter.asString(triples, "\n  ") ;
-        return printAbbrev(x) ; 
-    }
-
     public static Set<NodeId> convertToNodeIds(Collection<Node> nodes, DatasetGraphTDB dataset)
     {
         Set<NodeId> graphIds = new HashSet<>() ;
diff --git a/jena-tdb/src/main/java/org/apache/jena/tdb/solver/StageMatchTuple.java b/jena-tdb/src/main/java/org/apache/jena/tdb/solver/StageMatchTuple.java
index b19fd32..8054343 100644
--- a/jena-tdb/src/main/java/org/apache/jena/tdb/solver/StageMatchTuple.java
+++ b/jena-tdb/src/main/java/org/apache/jena/tdb/solver/StageMatchTuple.java
@@ -19,10 +19,9 @@
 package org.apache.jena.tdb.solver;
 
 
-import static org.apache.jena.atlas.lib.tuple.TupleFactory.* ;
+import static org.apache.jena.atlas.lib.tuple.TupleFactory.asTuple;
 
 import java.util.Iterator;
-import java.util.List;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -146,21 +145,6 @@
         return Iter.iter(iterMatches).map(binder).removeNulls() ;
     }
     
-    private static Iterator<Tuple<NodeId>> print(Iterator<Tuple<NodeId>> iter)
-    {
-        if ( ! iter.hasNext() )
-            System.err.println("<empty>") ;
-        else
-        {
-            List<Tuple<NodeId>> r = Iter.toList(iter) ;
-            String str = Iter.asString(r, "\n") ;
-            System.err.println(str) ;
-            // Reset iter
-            iter = Iter.iter(r) ;
-        }
-        return iter ;
-    }
-    
     private static boolean reject(BindingNodeId output , Var var, NodeId value)
     {
         if ( ! output.containsKey(var) )