Skip to content

Commit ae97b6f

Browse files
committed
binarySearch on sorted arrays and sorted lists
1 parent 9cac8b5 commit ae97b6f

File tree

4 files changed

+387
-0
lines changed

4 files changed

+387
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3;
18+
19+
import java.util.Comparator;
20+
import java.util.List;
21+
import java.util.function.Function;
22+
23+
24+
/**
25+
* Operations on sorted arrays.
26+
*/
27+
public class SortedArrayUtils {
28+
29+
/**
30+
* Finds element in sorted array.
31+
*
32+
* @param array
33+
* array sorted by key field
34+
* @param key
35+
* key to search for
36+
* @param keyExtractor
37+
* function to extract key from element
38+
* @param comparator
39+
* comparator for keys
40+
*
41+
* @return
42+
* index of the search key, if it is contained in the array within specified range; otherwise,
43+
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
44+
* are lower, the first_greater is defined as toIndex.
45+
*
46+
* @param <T>
47+
* type of array element
48+
* @param <K>
49+
* type of key
50+
*/
51+
public static <K, T> int binarySearch(
52+
T[] array,
53+
K key,
54+
Function<T, K> keyExtractor, Comparator<? super K> comparator
55+
) {
56+
return binarySearch(array, 0, array.length, key, keyExtractor, comparator);
57+
}
58+
59+
/**
60+
* Finds element in sorted array, within range fromIndex - toIndex (inclusive - exclusive).
61+
*
62+
* @param array
63+
* array sorted by key field
64+
* @param fromIndex
65+
* start index
66+
* @param toIndex
67+
* end index (exclusive)
68+
* @param key
69+
* key to search for
70+
* @param keyExtractor
71+
* function to extract key from element
72+
* @param comparator
73+
* comparator for keys
74+
*
75+
* @return
76+
* index of the search key, if it is contained in the array within specified range; otherwise,
77+
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
78+
* are lower, the first_greater is defined as toIndex.
79+
*
80+
* @param <T>
81+
* type of array element
82+
* @param <K>
83+
* type of key
84+
*/
85+
public static <T, K> int binarySearch(
86+
T[] array,
87+
int fromIndex, int toIndex,
88+
K key,
89+
Function<T, K> keyExtractor, Comparator<? super K> comparator
90+
) {
91+
int l = fromIndex;
92+
int h = toIndex - 1;
93+
94+
while (l <= h) {
95+
int m = (l + h) >>> 1; // unsigned shift to avoid overflow
96+
K value = keyExtractor.apply(array[m]);
97+
int c = comparator.compare(value, key);
98+
if (c < 0) {
99+
l = m + 1;
100+
} else if (c > 0) {
101+
h = m - 1;
102+
} else {
103+
// 0, found
104+
return m;
105+
}
106+
}
107+
108+
// not found, the l points to the lowest higher match:
109+
return -l - 1;
110+
}
111+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3;
18+
19+
import java.util.Comparator;
20+
import java.util.List;
21+
import java.util.function.Function;
22+
23+
24+
/**
25+
* Operations on sorted {@link List}.
26+
*/
27+
public class SortedListUtils {
28+
/**
29+
* Finds element in sorted list.
30+
*
31+
* @param list
32+
* list sorted by key field
33+
* @param key
34+
* key to search for
35+
* @param keyExtractor
36+
* function to extract key from element
37+
* @param comparator
38+
* comparator for keys
39+
*
40+
* @return
41+
* index of the search key, if it is contained in the list within specified range; otherwise,
42+
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
43+
* are lower, the first_greater is defined as toIndex.
44+
*
45+
* @param <T>
46+
* type of list element
47+
* @param <K>
48+
* type of key
49+
*/
50+
public static <K, T> int binarySearch(
51+
List<T> list,
52+
K key,
53+
Function<T, K> keyExtractor, Comparator<? super K> comparator
54+
) {
55+
return binarySearch(list, 0, list.size(), key, keyExtractor, comparator);
56+
}
57+
58+
/**
59+
* Finds element in sorted list, within range fromIndex - toIndex (inclusive - exclusive).
60+
*
61+
* @param list
62+
* list sorted by key field
63+
* @param fromIndex
64+
* start index
65+
* @param toIndex
66+
* end index (exclusive)
67+
* @param key
68+
* key to search for
69+
* @param keyExtractor
70+
* function to extract key from element
71+
* @param comparator
72+
* comparator for keys
73+
*
74+
* @return
75+
* index of the search key, if it is contained in the list within specified range; otherwise,
76+
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
77+
* are lower, the first_greater is defined as toIndex.
78+
*
79+
* @param <T>
80+
* type of array element
81+
* @param <K>
82+
* type of key
83+
*/
84+
public static <T, K> int binarySearch(
85+
List<T> list,
86+
int fromIndex, int toIndex,
87+
K key,
88+
Function<T, K> keyExtractor, Comparator<? super K> comparator
89+
) {
90+
int l = fromIndex;
91+
int h = toIndex - 1;
92+
93+
while (l <= h) {
94+
int m = (l + h) >>> 1; // unsigned shift to avoid overflow
95+
K value = keyExtractor.apply(list.get(m));
96+
int c = comparator.compare(value, key);
97+
if (c < 0) {
98+
l = m + 1;
99+
} else if (c > 0) {
100+
h = m - 1;
101+
} else {
102+
// 0, found
103+
return m;
104+
}
105+
}
106+
107+
// not found, the l points to the lowest higher match:
108+
return -l - 1;
109+
}
110+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import java.util.stream.IntStream;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
/**
26+
* Unit tests {@link SortedArrayUtils}.
27+
*/
28+
public class SortedArrayUtilsTest extends AbstractLangTest {
29+
30+
@Test
31+
public void binarySearch_whenEmpty_returnM1() {
32+
Data[] list = createList();
33+
int found = SortedArrayUtils.binarySearch(list, 0, Data::getValue, Integer::compare);
34+
assertEquals(-1, found);
35+
}
36+
37+
@Test
38+
public void binarySearch_whenExists_returnIndex() {
39+
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
40+
int found = SortedArrayUtils.binarySearch(list, 9, Data::getValue, Integer::compare);
41+
assertEquals(5, found);
42+
}
43+
44+
@Test
45+
public void binarySearch_whenNotExists_returnMinusInsertion() {
46+
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
47+
int found = SortedArrayUtils.binarySearch(list, 8, Data::getValue, Integer::compare);
48+
assertEquals(-6, found);
49+
}
50+
51+
@Test
52+
public void binarySearch_whenNotExistsBeginning_returnMinus1() {
53+
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
54+
int found = SortedArrayUtils.binarySearch(list, -3, Data::getValue, Integer::compare);
55+
assertEquals(-1, found);
56+
}
57+
58+
@Test
59+
public void binarySearch_whenNotExistsEnd_returnMinusLength() {
60+
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
61+
int found = SortedArrayUtils.binarySearch(list, 29, Data::getValue, Integer::compare);
62+
assertEquals(-(list.length + 1), found);
63+
}
64+
65+
private Data[] createList(int... values) {
66+
return IntStream.of(values).mapToObj(Data::new)
67+
.toArray(Data[]::new);
68+
}
69+
70+
public class Data
71+
{
72+
private final int value;
73+
74+
public Data(int value) {
75+
this.value = value;
76+
}
77+
78+
public int getValue() {
79+
return value;
80+
}
81+
}
82+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import java.util.List;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.IntStream;
24+
25+
import static org.junit.jupiter.api.Assertions.*;
26+
27+
/**
28+
* Unit tests {@link SortedListUtils}.
29+
*/
30+
public class SortedListUtilsTest extends AbstractLangTest {
31+
32+
@Test
33+
public void binarySearch_whenEmpty_returnM1() {
34+
List<Data> list = createList();
35+
int found = SortedListUtils.binarySearch(list, 0, Data::getValue, Integer::compare);
36+
assertEquals(-1, found);
37+
}
38+
39+
@Test
40+
public void binarySearch_whenExists_returnIndex() {
41+
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
42+
int found = SortedListUtils.binarySearch(list, 9, Data::getValue, Integer::compare);
43+
assertEquals(5, found);
44+
}
45+
46+
@Test
47+
public void binarySearch_whenNotExists_returnMinusInsertion() {
48+
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
49+
int found = SortedListUtils.binarySearch(list, 8, Data::getValue, Integer::compare);
50+
assertEquals(-6, found);
51+
}
52+
53+
@Test
54+
public void binarySearch_whenNotExistsBeginning_returnMinus1() {
55+
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
56+
int found = SortedListUtils.binarySearch(list, -3, Data::getValue, Integer::compare);
57+
assertEquals(-1, found);
58+
}
59+
60+
@Test
61+
public void binarySearch_whenNotExistsEnd_returnMinusLength() {
62+
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25);
63+
int found = SortedListUtils.binarySearch(list, 29, Data::getValue, Integer::compare);
64+
assertEquals(-(list.size() + 1), found);
65+
}
66+
67+
private List<Data> createList(int... values) {
68+
return IntStream.of(values).mapToObj(Data::new)
69+
.collect(Collectors.toList());
70+
}
71+
72+
public class Data
73+
{
74+
private final int value;
75+
76+
public Data(int value) {
77+
this.value = value;
78+
}
79+
80+
public int getValue() {
81+
return value;
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)