diff --git a/src/java.base/share/classes/java/util/Arrays.java b/src/java.base/share/classes/java/util/Arrays.java index 1bba844f791e8..a4f9539643d7a 100644 --- a/src/java.base/share/classes/java/util/Arrays.java +++ b/src/java.base/share/classes/java/util/Arrays.java @@ -4181,6 +4181,352 @@ public static List asList(T... a) { return new ArrayList<>(a); } + // ----------------------- WRAPPER ARRAYS ---------------------------- + + /** + * Returns the index of the first element in the sorted array not less than the key using the given comparator. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type + * @param arr the sorted array + * @param key the search key + * @param comp the comparator to compare elements + * @return index of the first element not less than key + */ + public static int lowerBound(T[] arr, T key, Comparator comp) { + int low = 0, high = arr.length - 1, idx = arr.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (comp.compare(arr[mid], key) >= 0) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Returns the index of the first element in the sorted array not less than the key using natural order. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type (comparable) + * @param arr the sorted array + * @param key the search key + * @return index of the first element not less than key + */ + public static > int lowerBound(T[] arr, T key) { + return lowerBound(arr, key, Comparator.naturalOrder()); + } + + /** + * Returns the index of the first element in the sorted array greater than the key using the given comparator. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type + * @param arr the sorted array + * @param key the search key + * @param comp the comparator to compare elements + * @return index of the first element greater than key + */ + public static int upperBound(T[] arr, T key, Comparator comp) { + int low = 0, high = arr.length - 1, idx = arr.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (comp.compare(arr[mid], key) > 0) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Returns the index of the first element in the sorted array greater than the key using natural order. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type (comparable) + * @param arr the sorted array + * @param key the search key + * @return index of the first element greater than key + */ + public static > int upperBound(T[] arr, T key) { + return upperBound(arr, key, Comparator.naturalOrder()); + } + + // ----------------------- PRIMITIVE ARRAYS ---------------------------- + + /** + * Returns the index of the first element in the sorted int array not less than key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array sorted int array + * @param key target value + * @return index of the first element >= key + */ + public static int lowerBound(int[] array, int key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] >= key) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Returns the index of the first element in the sorted int array greater than key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array sorted int array + * @param key target value + * @return index of the first element > key + */ + public static int upperBound(int[] array, int key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] > key) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Returns the index of the first element in the sorted long array not less than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted long array + * @param key the target value + * @return index of the first element greater than or equal to the key + */ + public static int lowerBound(long[] array, long key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] >= key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted long array strictly greater than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted long array + * @param key the target value + * @return index of the first element greater than the key + */ + public static int upperBound(long[] array, long key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] > key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted int array not less than key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array sorted int array + * @param key target value + * @return index of the first element >= key + */ + public static int lowerBound(short[] array, int key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] >= key) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Returns the index of the first element in the sorted int array greater than key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array sorted int array + * @param key target value + * @return index of the first element > key + */ + public static int upperBound(short[] array, int key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] > key) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Returns the index of the first element in the sorted double array not less than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted double array + * @param key the target value + * @return index of the first element greater than or equal to the key + */ + public static int lowerBound(double[] array, double key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] >= key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted double array strictly greater than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted double array + * @param key the target value + * @return index of the first element greater than the key + */ + public static int upperBound(double[] array, double key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] > key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted float array not less than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted float array + * @param key the target value + * @return index of the first element greater than or equal to the key + */ + public static int lowerBound(float[] array, float key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] >= key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted float array strictly greater than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted float array + * @param key the target value + * @return index of the first element greater than the key + */ + public static int upperBound(float[] array, float key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] > key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted char array not less than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted char array + * @param key the target value + * @return index of the first element greater than or equal to the key + */ + public static int lowerBound(char[] array, char key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] >= key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + + /** + * Returns the index of the first element in the sorted char array strictly greater than the given key. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param array the sorted char array + * @param key the target value + * @return index of the first element greater than the key + */ + public static int upperBound(char[] array, char key) { + int low = 0, high = array.length - 1, idx = array.length; + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (array[mid] > key) { + idx = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return idx; + } + /** * @serial include */ diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index 4dc7edeb85271..40fb41423fd60 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -1047,6 +1047,84 @@ public static Collection unmodifiableCollection(Collection c return new UnmodifiableCollection<>(c); } + /** + * Finds the index of the first element in the sorted list that is not less than the specified key, + * using the given comparator. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type + * @param list the sorted list to be searched + * @param key the target value + * @param comp the comparator to determine order + * @return the index of the first element not less than key + */ + public static int lowerBound(List list, T key, Comparator comp) { + int low = 0, high = list.size() - 1, idx = list.size(); + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (comp.compare(list.get(mid), key) >= 0) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Finds the index of the first element in the sorted list that is not less than the specified key, + * using natural ordering. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type, must be comparable + * @param list the sorted list to be searched + * @param key the target value + * @return the index of the first element not less than key + */ + public static > int lowerBound(List list, T key) { + return lowerBound(list, key, Comparator.naturalOrder()); + } + + /** + * Finds the index of the first element in the sorted list that is greater than the specified key, + * using the given comparator. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type + * @param list the sorted list to be searched + * @param key the target value + * @param comp the comparator to determine order + * @return the index of the first element greater than key + */ + public static int upperBound(List list, T key, Comparator comp) { + int low = 0, high = list.size() - 1, idx = list.size(); + while (low <= high) { + int mid = low + ((high - low) >> 1); + if (comp.compare(list.get(mid), key) > 0) { + idx = mid; + high = mid - 1; + } else low = mid + 1; + } + return idx; + } + + /** + * Finds the index of the first element in the sorted list that is greater than the specified key, + * using natural ordering. + * @implNote This implementation uses binary search and runs in O(log n) time. + * Contributed by Adarsh Sharma. + * + * @param the element type, must be comparable + * @param list the sorted list to be searched + * @param key the target value + * @return the index of the first element greater than key + */ + public static > int upperBound(List list, T key) { + return upperBound(list, key, Comparator.naturalOrder()); + } + /** * @serial include */ diff --git a/test/jdk/java/util/Arrays/BoundUtilsTest.java b/test/jdk/java/util/Arrays/BoundUtilsTest.java new file mode 100644 index 0000000000000..5d8908ef5cfd3 --- /dev/null +++ b/test/jdk/java/util/Arrays/BoundUtilsTest.java @@ -0,0 +1,158 @@ +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; + +/** + * Unit tests for lowerBound() and upperBound() utility methods. + *

+ * Covers edge cases for: + * - Wrapper arrays + * - Primitive arrays (int[], long[], etc.) + *

+ * @author Adarsh Sharma + * @since 1.0 + */ +public class BoundsUtilsTest { + + // ---------------------------------------- + // Wrapper array tests (Integer[]) + // ---------------------------------------- + + @Test + public void testLowerBound_WrapperArray_Empty() { + Integer[] array = new Integer[]{}; + Assert.assertEquals(Arrays.lowerBound(array, 5), array.length); + } + + @Test + public void testUpperBound_WrapperArray_Empty() { + Integer[] array = new Integer[]{}; + Assert.assertEquals(Arrays.upperBound(array, 5), array.length); + } + + @Test + public void testLowerBound_WrapperArray_NormalCases() { + Integer[] array = new Integer[]{2, 4, 6, 8}; + Assert.assertEquals(Arrays.lowerBound(array, 3), 1); + Assert.assertEquals(Arrays.lowerBound(array, 8), 3); + Assert.assertEquals(Arrays.lowerBound(array, 10), array.length); + } + + @Test + public void testUpperBound_WrapperArray_NormalCases() { + Integer[] array = new Integer[]{2, 4, 6, 8}; + Assert.assertEquals(Arrays.upperBound(array, 2), 1); + Assert.assertEquals(Arrays.upperBound(array, 7), 3); + Assert.assertEquals(Arrays.upperBound(array, 8), array.length); + } + + // ---------------------------------------- + // Primitive Arrays + // ---------------------------------------- + + // int[] + @Test + public void testLowerBound_IntArray() { + int[] array = {1, 3, 5, 7}; + Assert.assertEquals(Arrays.lowerBound(array, 5), 2); + Assert.assertEquals(Arrays.lowerBound(array, 8), array.length); + } + + @Test + public void testUpperBound_IntArray() { + int[] array = {1, 3, 5, 7}; + Assert.assertEquals(Arrays.upperBound(array, 5), 3); + Assert.assertEquals(Arrays.upperBound(array, 7), array.length); + } + + // long[] + @Test + public void testLowerBound_LongArray() { + long[] array = {1L, 3L, 5L, 7L}; + Assert.assertEquals(Arrays.lowerBound(array, 4L), 2); + Assert.assertEquals(Arrays.lowerBound(array, 10L), array.length); + } + + @Test + public void testUpperBound_LongArray() { + long[] array = {1L, 3L, 5L, 7L}; + Assert.assertEquals(Arrays.upperBound(array, 3L), 2); + Assert.assertEquals(Arrays.upperBound(array, 7L), array.length); + } + + // char[] + @Test + public void testLowerBound_CharArray() { + char[] array = {'a', 'c', 'e'}; + Assert.assertEquals(Arrays.lowerBound(array, 'b'), 1); + Assert.assertEquals(Arrays.lowerBound(array, 'f'), array.length); + } + + @Test + public void testUpperBound_CharArray() { + char[] array = {'a', 'c', 'e'}; + Assert.assertEquals(Arrays.upperBound(array, 'c'), 2); + Assert.assertEquals(Arrays.upperBound(array, 'e'), array.length); + } + + // short[] + @Test + public void testLowerBound_ShortArray() { + short[] array = {1, 2, 4, 6}; + Assert.assertEquals(Arrays.lowerBound(array, (short) 3), 2); + Assert.assertEquals(Arrays.lowerBound(array, (short) 7), array.length); + } + + @Test + public void testUpperBound_ShortArray() { + short[] array = {1, 2, 4, 6}; + Assert.assertEquals(Arrays.upperBound(array, (short) 2), 2); + Assert.assertEquals(Arrays.upperBound(array, (short) 6), array.length); + } + + // float[] + @Test + public void testLowerBound_FloatArray() { + float[] array = {1.1f, 2.2f, 4.4f}; + Assert.assertEquals(Arrays.lowerBound(array, 2.5f), 2); + Assert.assertEquals(Arrays.lowerBound(array, 5.0f), array.length); + } + + @Test + public void testUpperBound_FloatArray() { + float[] array = {1.1f, 2.2f, 4.4f}; + Assert.assertEquals(Arrays.upperBound(array, 2.2f), 2); + Assert.assertEquals(Arrays.upperBound(array, 4.4f), array.length); + } + + // double[] + @Test + public void testLowerBound_DoubleArray() { + double[] array = {1.0, 3.0, 5.0}; + Assert.assertEquals(Arrays.lowerBound(array, 4.0), 2); + Assert.assertEquals(Arrays.lowerBound(array, 6.0), array.length); + } + + @Test + public void testUpperBound_DoubleArray() { + double[] array = {1.0, 3.0, 5.0}; + Assert.assertEquals(Arrays.upperBound(array, 3.0), 2); + Assert.assertEquals(Arrays.upperBound(array, 5.0), array.length); + } + + // String[] (Object array) + @Test + public void testLowerBound_StringArray() { + String[] array = {"apple", "banana", "cherry"}; + Assert.assertEquals(Arrays.lowerBound(array, "banana"), 1); + Assert.assertEquals(Arrays.lowerBound(array, "coconut"), 2); + } + + @Test + public void testUpperBound_StringArray() { + String[] array = {"apple", "banana", "cherry"}; + Assert.assertEquals(Arrays.upperBound(array, "banana"), 2); + Assert.assertEquals(Arrays.upperBound(array, "cherry"), array.length); + } +} diff --git a/test/jdk/java/util/Collections/BoundUtilsTest.java b/test/jdk/java/util/Collections/BoundUtilsTest.java new file mode 100644 index 0000000000000..b802c3f65a635 --- /dev/null +++ b/test/jdk/java/util/Collections/BoundUtilsTest.java @@ -0,0 +1,59 @@ +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for lowerBound() and upperBound() utility methods. + *

+ * Covers edge cases for: + * - Collection + *

+ * @author Adarsh Sharma + * @since 1.0 + */ +public class BoundUtilsTest { + + /** + * Tests lowerBound with an empty collection. + */ + @Test + public void testLowerBound_Collection_Empty() { + List list = Collections.emptyList(); + int index = Collections.lowerBound(list, 10); + Assert.assertEquals(index, list.size()); + } + + /** + * Tests upperBound with an empty collection. + */ + @Test + public void testUpperBound_Collection_Empty() { + List list = Collections.emptyList(); + int index = Collections.upperBound(list, 10); + Assert.assertEquals(index, list.size()); + } + + /** + * Tests lowerBound at start, middle and end of sorted collection. + */ + @Test + public void testLowerBound_Collection_NormalCases() { + List list = Arrays.asList(1, 3, 5, 7, 9); + Assert.assertEquals(Collections.lowerBound(list, 1), 0); // Exact match at start + Assert.assertEquals(Collections.lowerBound(list, 4), 2); // Between 3 and 5 + Assert.assertEquals(Collections.lowerBound(list, 10), list.size()); // Out of bounds + } + + /** + * Tests upperBound at start, middle and end of sorted collection. + */ + @Test + public void testUpperBound_Collection_NormalCases() { + List list = Arrays.asList(1, 3, 5, 7, 9); + Assert.assertEquals(Collections.upperBound(list, 1), 1); // Next index after 1 + Assert.assertEquals(Collections.upperBound(list, 4), 2); // Next after 3 + Assert.assertEquals(Collections.upperBound(list, 9), list.size()); // No greater element + } +}