Add findFirst and findLast to CollectionsUtils

Test: new tests in this patch
Change-Id: Ie13ee7f50832ffec1febe4f4596d0b26e4c4a2f6
diff --git a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
index a16ef33..73f6cd8 100644
--- a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
@@ -22,6 +22,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
 
@@ -219,4 +220,38 @@
         return haystack.containsAll(needles);
     }
 
+    /**
+     * Returns the first item of a collection that matches the predicate.
+     * @param haystack The collection to search.
+     * @param condition The predicate to match.
+     * @param <T> The type of element in the collection.
+     * @return The first element matching the predicate, or null if none.
+     */
+    @Nullable
+    public static <T> T findFirst(Collection<T> haystack, Predicate<? super T> condition) {
+        for (T needle : haystack) {
+            if (condition.test(needle)) return needle;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the last item of a List that matches the predicate.
+     * @param haystack The List to search.
+     * @param condition The predicate to match.
+     * @param <T> The type of element in the list.
+     * @return The last element matching the predicate, or null if none.
+     */
+    // There is no way to reverse iterate a Collection in Java (e.g. the collection may
+    // be a single-linked list), so implementing this on Collection is necessarily very
+    // wasteful (store and reverse a copy, test all elements, or recurse to the end of the
+    // list to test on the up path and possibly blow the call stack)
+    @Nullable
+    public static <T> T findLast(List<T> haystack, Predicate<? super T> condition) {
+        for (int i = haystack.size() - 1; i >= 0; --i) {
+            final T needle = haystack.get(i);
+            if (condition.test(needle)) return needle;
+        }
+        return null;
+    }
 }
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
index 911483a..2de92f4 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
+import kotlin.test.assertNull
+import kotlin.test.assertSame
 import kotlin.test.assertTrue
 
 @RunWith(AndroidJUnit4::class)
@@ -86,4 +88,27 @@
         assertEquals(3, CollectionUtils.total(longArrayOf(1, 1, 1)))
         assertEquals(0, CollectionUtils.total(null))
     }
+
+    @Test
+    fun testFindFirstFindLast() {
+        val listAE = listOf("A", "B", "C", "D", "E")
+        assertSame(CollectionUtils.findFirst(listAE) { it == "A" }, listAE[0])
+        assertSame(CollectionUtils.findFirst(listAE) { it == "B" }, listAE[1])
+        assertSame(CollectionUtils.findFirst(listAE) { it == "E" }, listAE[4])
+        assertNull(CollectionUtils.findFirst(listAE) { it == "F" })
+        assertSame(CollectionUtils.findLast(listAE) { it == "A" }, listAE[0])
+        assertSame(CollectionUtils.findLast(listAE) { it == "B" }, listAE[1])
+        assertSame(CollectionUtils.findLast(listAE) { it == "E" }, listAE[4])
+        assertNull(CollectionUtils.findLast(listAE) { it == "F" })
+
+        val listMulti = listOf("A", "B", "A", "C", "D", "E", "A", "E")
+        assertSame(CollectionUtils.findFirst(listMulti) { it == "A" }, listMulti[0])
+        assertSame(CollectionUtils.findFirst(listMulti) { it == "B" }, listMulti[1])
+        assertSame(CollectionUtils.findFirst(listMulti) { it == "E" }, listMulti[5])
+        assertNull(CollectionUtils.findFirst(listMulti) { it == "F" })
+        assertSame(CollectionUtils.findLast(listMulti) { it == "A" }, listMulti[6])
+        assertSame(CollectionUtils.findLast(listMulti) { it == "B" }, listMulti[1])
+        assertSame(CollectionUtils.findLast(listMulti) { it == "E" }, listMulti[7])
+        assertNull(CollectionUtils.findLast(listMulti) { it == "F" })
+    }
 }