diff --git a/src/org/openstreetmap/josm/tools/Utils.java b/src/org/openstreetmap/josm/tools/Utils.java
index fb4f678..16bc79d 100644
--- a/src/org/openstreetmap/josm/tools/Utils.java
+++ b/src/org/openstreetmap/josm/tools/Utils.java
@@ -50,6 +50,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.StringJoiner;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.ForkJoinWorkerThread;
@@ -57,7 +58,10 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -104,6 +108,15 @@ private Utils() {
     }
 
     /**
+     * Returns a sequential {@code Stream} with this collection as its source.
+     *
+     * @see Collection#stream()
+     */
+    private static <T> Stream<T> stream(Iterable<T> collection) {
+        return StreamSupport.stream(collection.spliterator(), false);
+    }
+
+    /**
      * Tests whether {@code predicate} applies to at least one element from {@code collection}.
      * <p>
      * Note: you can use {@link Stream#anyMatch(java.util.function.Predicate)} instead.
@@ -111,14 +124,12 @@ private Utils() {
      * @param collection the collection
      * @param predicate the predicate
      * @return {@code true} if {@code predicate} applies to at least one element from {@code collection}
+     * @deprecated Use {@link Stream#anyMatch} instead.
      */
+    @Deprecated
     public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) {
-        for (T item : collection) {
-            if (predicate.evaluate(item)) {
-                return true;
-            }
-        }
-        return false;
+        return stream(collection)
+                .anyMatch(predicate);
     }
 
     /**
@@ -129,9 +140,12 @@ private Utils() {
      * @param collection the collection
      * @param predicate the predicate
      * @return {@code true} if {@code predicate} applies to all elements from {@code collection}
+     * @deprecated Use {@link Stream#allMatch} instead.
      */
+    @Deprecated
     public static <T> boolean forAll(Iterable<? extends T> collection, Predicate<? super T> predicate) {
-        return !exists(collection, Predicates.not(predicate));
+        return stream(collection)
+                .allMatch(predicate);
     }
 
     /**
@@ -140,9 +154,11 @@ private Utils() {
      * @param collection The collection
      * @param clazz The class to search for.
      * @return <code>true</code> if that item exists in the collection.
+     * @deprecated Use {@link Stream#anyMatch} instead.
      */
     public static <T> boolean exists(Iterable<T> collection, Class<? extends T> clazz) {
-        return exists(collection, Predicates.<T>isInstanceOf(clazz));
+        return stream(collection)
+                .anyMatch(clazz::isInstance);
     }
 
     /**
@@ -153,12 +169,10 @@ private Utils() {
      * @return the item or <code>null</code> if there was not match.
      */
     public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) {
-        for (T item : collection) {
-            if (predicate.evaluate(item)) {
-                return item;
-            }
-        }
-        return null;
+        return stream(collection)
+                .filter(predicate)
+                .findFirst()
+                .orElse(null);
     }
 
     /**
@@ -196,12 +210,10 @@ private Utils() {
      */
     @SafeVarargs
     public static <T> T firstNonNull(T... items) {
-        for (T i : items) {
-            if (i != null) {
-                return i;
-            }
-        }
-        return null;
+        return Stream.of(items)
+                .filter(Objects::nonNull)
+                .findFirst()
+                .orElse(null);
     }
 
     /**
@@ -218,6 +230,19 @@ private Utils() {
     }
 
     /**
+     * Filter an iterable by (sub)class.
+     *
+     * @param <S> Super type of items
+     * @param <T> type of items
+     * @param iterable the iterable to filter
+     * @param clazz the (sub)class
+     * @return a stream of (sub)class elements
+     */
+    public static <S, T extends S> Stream<T> filter(Iterable<S> iterable, Class<T> clazz) {
+        return stream(iterable).filter(clazz::isInstance).map(clazz::cast);
+    }
+
+    /**
      * Find the index of the first item that matches the predicate.
      * @param <T> The iterable type
      * @param collection The iterable to iterate over.
@@ -306,23 +331,13 @@ public static int mod(int a, int n) {
      * @param values collection of objects, null is converted to the
      *  empty string
      * @return null if values is null. The joined string otherwise.
+     * @deprecated Use {@link String#join} or {@link Collectors#joining} instead.
      */
+    @Deprecated
     public static String join(String sep, Collection<?> values) {
-        CheckParameterUtil.ensureParameterNotNull(sep, "sep");
-        if (values == null)
-            return null;
-        StringBuilder s = null;
-        for (Object a : values) {
-            if (a == null) {
-                a = "";
-            }
-            if (s != null) {
-                s.append(sep).append(a);
-            } else {
-                s = new StringBuilder(a.toString());
-            }
-        }
-        return s != null ? s.toString() : "";
+        return values.stream()
+                .map(Objects::toString)
+                .collect(Collectors.joining(sep));
     }
 
     /**
@@ -331,13 +346,12 @@ public static String join(String sep, Collection<?> values) {
      * @return An unordered HTML list
      */
     public static String joinAsHtmlUnorderedList(Iterable<?> values) {
-        StringBuilder sb = new StringBuilder(1024);
-        sb.append("<ul>");
-        for (Object i : values) {
-            sb.append("<li>").append(i).append("</li>");
-        }
-        sb.append("</ul>");
-        return sb.toString();
+        return stream(values)
+                .map(Objects::toString)
+                .collect(Collector.of(
+                        () -> new StringJoiner("</li><li>", "<ul><li>", "</li></ul>").setEmptyValue("<ul></ul>"),
+                        StringJoiner::add, StringJoiner::merge, StringJoiner::toString
+                ));
     }
 
     /**
diff --git a/test/unit/org/openstreetmap/josm/tools/UtilsTest.java b/test/unit/org/openstreetmap/josm/tools/UtilsTest.java
index cbe2b8b..89bec7a 100644
--- a/test/unit/org/openstreetmap/josm/tools/UtilsTest.java
+++ b/test/unit/org/openstreetmap/josm/tools/UtilsTest.java
@@ -192,4 +192,15 @@ public void testSizeStringNegative() throws Exception {
         Utils.getSizeString(-1, Locale.ENGLISH);
     }
 
+    /**
+     * Test of {@link Utils#joinAsHtmlUnorderedList(Iterable)} method.
+     */
+    @Test
+    public void testJoinAsHtmlUnorderedList() {
+        assertEquals("<ul></ul>", Utils.joinAsHtmlUnorderedList(Arrays.asList()));
+        assertEquals("<ul><li>one</li></ul>", Utils.joinAsHtmlUnorderedList(Arrays.asList("one")));
+        assertEquals("<ul><li>one</li><li>two</li></ul>", Utils.joinAsHtmlUnorderedList(Arrays.asList("one", "two")));
+        assertEquals("<ul><li>one</li><li>two</li><li>many</li></ul>", Utils.joinAsHtmlUnorderedList(Arrays.asList("one", "two", "many")));
+    }
+
 }
