Skip to content

Commit fac9864

Browse files
committed
Add unboxing binarySearch to Lists util
1 parent 26282c6 commit fac9864

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>software.coley</groupId>
88
<artifactId>extra-collections</artifactId>
9-
<version>1.6.0</version>
9+
<version>1.7.0</version>
1010

1111
<name>Extra Collections</name>
1212
<description>Extra useful collection types and utilities</description>

src/main/java/software/coley/collections/Lists.java

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import java.util.Comparator;
1111
import java.util.List;
1212
import java.util.SortedSet;
13-
import java.util.TreeSet;
13+
import java.util.function.Function;
1414

1515
/**
1616
* Utility for handling {@link java.util.List} types.
@@ -201,8 +201,9 @@ public static <T> List<T> ofVar(T... values) {
201201
* @param <T>
202202
* Item type.
203203
*
204-
* @return Index of item in list.
205-
* If the item is not in the list, the negative value of the index where it would appear in sorted order.
204+
* @return Index of item in list, within the range.
205+
* If the item is not in the list, then {@code (-(insertion point) - 1)} where the insertion point is
206+
* the index at which the value would be inserted into the list.
206207
*/
207208
public static <T extends Comparable<T>> int binarySearch(@Nonnull List<T> items, @Nonnull T target) {
208209
return binarySearch(items, target, 0, items.size() - 1);
@@ -221,24 +222,76 @@ public static <T extends Comparable<T>> int binarySearch(@Nonnull List<T> items,
221222
* Item type.
222223
*
223224
* @return Index of item in list, within the range.
224-
* If the item is not in the list, the negative value of the index where it would appear in sorted order.
225+
* If the item is not in the list, then {@code (-(insertion point) - 1)} where the insertion point is
226+
* the index at which the value would be inserted into the list.
225227
*/
226228
public static <T extends Comparable<T>> int binarySearch(@Nonnull List<T> items, @Nonnull T target, int first, int last) {
227-
if (first > last)
228-
// Typically yield '-1' but with this, we will have it such that if 'target' is not in the list
229-
// then the return value will be the negative value of the index where it would be inserted into
230-
// while maintaining sorted order.
231-
return (first == 0 && last == -1) ? last : -last;
232-
else {
233-
int middle = (first + last) / 2;
229+
while (first <= last) {
230+
int middle = (first + last) >>> 1;
234231
int compResult = target.compareTo(items.get(middle));
235232
if (compResult == 0)
236233
return middle;
237-
else if (compResult < 0)
238-
return binarySearch(items, target, first, middle - 1);
234+
if (compResult < 0)
235+
last = middle - 1;
236+
else
237+
first = middle + 1;
238+
}
239+
return (first == 0 && last == -1) ? last : -last;
240+
}
241+
242+
/**
243+
* @param items
244+
* Item list to search in.
245+
* @param target
246+
* Item to search for.
247+
* @param unbox
248+
* Function to unbox B into T.
249+
* @param <B>
250+
* Box type.
251+
* @param <T>
252+
* Item type.
253+
*
254+
* @return Index of item in list, within the range.
255+
* If the item is not in the list, then {@code (-(insertion point) - 1)} where the insertion point is
256+
* the index at which the value would be inserted into the list.
257+
*/
258+
public static <B, T extends Comparable<T>> int binaryUnboxingSearch(@Nonnull List<B> items, @Nonnull T target, @Nonnull Function<B, T> unbox) {
259+
return binaryUnboxingSearch(items, target, unbox, 0, items.size() - 1);
260+
}
261+
262+
/**
263+
* @param items
264+
* Item list to search in.
265+
* @param target
266+
* Item to search for.
267+
* @param unbox
268+
* Function to unbox B into T.
269+
* @param first
270+
* Start range.
271+
* @param last
272+
* End range.
273+
* @param <B>
274+
* Box type.
275+
* @param <T>
276+
* Item type.
277+
*
278+
* @return Index of item in list, within the range.
279+
* If the item is not in the list, then {@code (-(insertion point) - 1)} where the insertion point is
280+
* the index at which the value would be inserted into the list.
281+
*/
282+
public static <B, T extends Comparable<T>> int binaryUnboxingSearch(@Nonnull List<B> items, @Nonnull T target,
283+
@Nonnull Function<B, T> unbox, int first, int last) {
284+
while (first <= last) {
285+
int middle = (first + last) >>> 1;
286+
int compResult = target.compareTo(unbox.apply(items.get(middle)));
287+
if (compResult == 0)
288+
return middle;
289+
if (compResult < 0)
290+
last = middle - 1;
239291
else
240-
return binarySearch(items, target, middle + 1, last);
292+
first = middle + 1;
241293
}
294+
return (first == 0 && last == -1) ? last : -last;
242295
}
243296

244297
/**

src/test/java/software/coley/collections/ListsTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package software.coley.collections;
22

33
import org.junit.jupiter.api.Test;
4+
import software.coley.collections.box.Box;
45

56
import java.util.ArrayList;
7+
import java.util.Collections;
68
import java.util.Comparator;
79
import java.util.List;
10+
import java.util.function.Function;
811

912
import static java.util.Arrays.asList;
1013
import static java.util.Collections.emptyList;
@@ -72,6 +75,10 @@ void of() {
7275

7376
@Test
7477
void binarySearch() {
78+
// Base case, empty list
79+
assertEquals(-1, Lists.binarySearch(Collections.emptyList(), "a"));
80+
81+
// Alphabetical test case
7582
List<String> strings = asList("a", "b", "c", /* d */ "e", "f");
7683
assertEquals(0, Lists.binarySearch(strings, "a"));
7784
assertEquals(2, Lists.binarySearch(strings, "c"));
@@ -89,6 +96,23 @@ void binarySearch() {
8996
assertEquals(-3, Lists.binarySearch(strings, "a", 4, 5));
9097
}
9198

99+
@Test
100+
void binaryBoxedSearch() {
101+
// Same test as the above but boxed
102+
List<Box<String>> strings = asList(new Box<>("a"), new Box<>("b"), new Box<>("c"), /* new Box<>("d") */ new Box<>("e"), new Box<>("f"));
103+
assertEquals(0, Lists.binaryUnboxingSearch(strings, "a", Box::get));
104+
assertEquals(2, Lists. binaryUnboxingSearch(strings, "c", Box::get));
105+
assertEquals(3, Lists. binaryUnboxingSearch(strings, "e", Box::get));
106+
assertEquals(4, Lists. binaryUnboxingSearch(strings, "f", Box::get));
107+
assertEquals(-1, Lists.binaryUnboxingSearch(strings, " ", Box::get));
108+
assertEquals(-2, Lists.binaryUnboxingSearch(strings, "d", Box::get));
109+
assertEquals(-4, Lists.binaryUnboxingSearch(strings, "g", Box::get));
110+
assertEquals(0, Lists. binaryUnboxingSearch(strings, "a", Box::get, 1, 5));
111+
assertEquals(-1, Lists.binaryUnboxingSearch(strings, "a", Box::get, 2, 5));
112+
assertEquals(-2, Lists.binaryUnboxingSearch(strings, "a", Box::get, 3, 5));
113+
assertEquals(-3, Lists.binaryUnboxingSearch(strings, "a", Box::get, 4, 5));
114+
}
115+
92116
@Test
93117
void sortedInsertIndex() {
94118
// Unlike the binary search call, this normalizes the index to positive range

0 commit comments

Comments
 (0)