-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
binarySearch on sorted arrays and sorted lists
- Loading branch information
Showing
4 changed files
with
387 additions
and
0 deletions.
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
src/main/java/org/apache/commons/lang3/SortedArrayUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.commons.lang3; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
|
||
|
||
/** | ||
* Operations on sorted arrays. | ||
*/ | ||
public class SortedArrayUtils { | ||
|
||
/** | ||
* Finds element in sorted array. | ||
* | ||
* @param array | ||
* array sorted by key field | ||
* @param key | ||
* key to search for | ||
* @param keyExtractor | ||
* function to extract key from element | ||
* @param comparator | ||
* comparator for keys | ||
* | ||
* @return | ||
* index of the search key, if it is contained in the array within specified range; otherwise, | ||
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements | ||
* are lower, the first_greater is defined as toIndex. | ||
* | ||
* @param <T> | ||
* type of array element | ||
* @param <K> | ||
* type of key | ||
*/ | ||
public static <K, T> int binarySearch( | ||
T[] array, | ||
K key, | ||
Function<T, K> keyExtractor, Comparator<? super K> comparator | ||
) { | ||
return binarySearch(array, 0, array.length, key, keyExtractor, comparator); | ||
} | ||
|
||
/** | ||
* Finds element in sorted array, within range fromIndex - toIndex (inclusive - exclusive). | ||
* | ||
* @param array | ||
* array sorted by key field | ||
* @param fromIndex | ||
* start index | ||
* @param toIndex | ||
* end index (exclusive) | ||
* @param key | ||
* key to search for | ||
* @param keyExtractor | ||
* function to extract key from element | ||
* @param comparator | ||
* comparator for keys | ||
* | ||
* @return | ||
* index of the search key, if it is contained in the array within specified range; otherwise, | ||
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements | ||
* are lower, the first_greater is defined as toIndex. | ||
* | ||
* @param <T> | ||
* type of array element | ||
* @param <K> | ||
* type of key | ||
*/ | ||
public static <T, K> int binarySearch( | ||
T[] array, | ||
int fromIndex, int toIndex, | ||
K key, | ||
Function<T, K> keyExtractor, Comparator<? super K> comparator | ||
) { | ||
int l = fromIndex; | ||
int h = toIndex - 1; | ||
|
||
while (l <= h) { | ||
int m = (l + h) >>> 1; // unsigned shift to avoid overflow | ||
K value = keyExtractor.apply(array[m]); | ||
int c = comparator.compare(value, key); | ||
if (c < 0) { | ||
l = m + 1; | ||
} else if (c > 0) { | ||
h = m - 1; | ||
} else { | ||
// 0, found | ||
return m; | ||
} | ||
} | ||
|
||
// not found, the l points to the lowest higher match: | ||
return -l - 1; | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
src/main/java/org/apache/commons/lang3/SortedListUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.commons.lang3; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
|
||
|
||
/** | ||
* Operations on sorted {@link List}. | ||
*/ | ||
public class SortedListUtils { | ||
/** | ||
* Finds element in sorted list. | ||
* | ||
* @param list | ||
* list sorted by key field | ||
* @param key | ||
* key to search for | ||
* @param keyExtractor | ||
* function to extract key from element | ||
* @param comparator | ||
* comparator for keys | ||
* | ||
* @return | ||
* index of the search key, if it is contained in the list within specified range; otherwise, | ||
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements | ||
* are lower, the first_greater is defined as toIndex. | ||
* | ||
* @param <T> | ||
* type of list element | ||
* @param <K> | ||
* type of key | ||
*/ | ||
public static <K, T> int binarySearch( | ||
List<T> list, | ||
K key, | ||
Function<T, K> keyExtractor, Comparator<? super K> comparator | ||
) { | ||
return binarySearch(list, 0, list.size(), key, keyExtractor, comparator); | ||
} | ||
|
||
/** | ||
* Finds element in sorted list, within range fromIndex - toIndex (inclusive - exclusive). | ||
* | ||
* @param list | ||
* list sorted by key field | ||
* @param fromIndex | ||
* start index | ||
* @param toIndex | ||
* end index (exclusive) | ||
* @param key | ||
* key to search for | ||
* @param keyExtractor | ||
* function to extract key from element | ||
* @param comparator | ||
* comparator for keys | ||
* | ||
* @return | ||
* index of the search key, if it is contained in the list within specified range; otherwise, | ||
* (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements | ||
* are lower, the first_greater is defined as toIndex. | ||
* | ||
* @param <T> | ||
* type of array element | ||
* @param <K> | ||
* type of key | ||
*/ | ||
public static <T, K> int binarySearch( | ||
List<T> list, | ||
int fromIndex, int toIndex, | ||
K key, | ||
Function<T, K> keyExtractor, Comparator<? super K> comparator | ||
) { | ||
int l = fromIndex; | ||
int h = toIndex - 1; | ||
|
||
while (l <= h) { | ||
int m = (l + h) >>> 1; // unsigned shift to avoid overflow | ||
K value = keyExtractor.apply(list.get(m)); | ||
int c = comparator.compare(value, key); | ||
if (c < 0) { | ||
l = m + 1; | ||
} else if (c > 0) { | ||
h = m - 1; | ||
} else { | ||
// 0, found | ||
return m; | ||
} | ||
} | ||
|
||
// not found, the l points to the lowest higher match: | ||
return -l - 1; | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
src/test/java/org/apache/commons/lang3/SortedArrayUtilsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.commons.lang3; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.stream.IntStream; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
/** | ||
* Unit tests {@link SortedArrayUtils}. | ||
*/ | ||
public class SortedArrayUtilsTest extends AbstractLangTest { | ||
|
||
@Test | ||
public void binarySearch_whenEmpty_returnM1() { | ||
Data[] list = createList(); | ||
int found = SortedArrayUtils.binarySearch(list, 0, Data::getValue, Integer::compare); | ||
assertEquals(-1, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenExists_returnIndex() { | ||
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedArrayUtils.binarySearch(list, 9, Data::getValue, Integer::compare); | ||
assertEquals(5, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenNotExists_returnMinusInsertion() { | ||
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedArrayUtils.binarySearch(list, 8, Data::getValue, Integer::compare); | ||
assertEquals(-6, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenNotExistsBeginning_returnMinus1() { | ||
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedArrayUtils.binarySearch(list, -3, Data::getValue, Integer::compare); | ||
assertEquals(-1, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenNotExistsEnd_returnMinusLength() { | ||
Data[] list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedArrayUtils.binarySearch(list, 29, Data::getValue, Integer::compare); | ||
assertEquals(-(list.length + 1), found); | ||
} | ||
|
||
private Data[] createList(int... values) { | ||
return IntStream.of(values).mapToObj(Data::new) | ||
.toArray(Data[]::new); | ||
} | ||
|
||
public class Data | ||
{ | ||
private final int value; | ||
|
||
public Data(int value) { | ||
this.value = value; | ||
} | ||
|
||
public int getValue() { | ||
return value; | ||
} | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/test/java/org/apache/commons/lang3/SortedListUtilsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.commons.lang3; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
|
||
/** | ||
* Unit tests {@link SortedListUtils}. | ||
*/ | ||
public class SortedListUtilsTest extends AbstractLangTest { | ||
|
||
@Test | ||
public void binarySearch_whenEmpty_returnM1() { | ||
List<Data> list = createList(); | ||
int found = SortedListUtils.binarySearch(list, 0, Data::getValue, Integer::compare); | ||
assertEquals(-1, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenExists_returnIndex() { | ||
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedListUtils.binarySearch(list, 9, Data::getValue, Integer::compare); | ||
assertEquals(5, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenNotExists_returnMinusInsertion() { | ||
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedListUtils.binarySearch(list, 8, Data::getValue, Integer::compare); | ||
assertEquals(-6, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenNotExistsBeginning_returnMinus1() { | ||
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedListUtils.binarySearch(list, -3, Data::getValue, Integer::compare); | ||
assertEquals(-1, found); | ||
} | ||
|
||
@Test | ||
public void binarySearch_whenNotExistsEnd_returnMinusLength() { | ||
List<Data> list = createList(0, 1, 2, 4, 7, 9, 12, 15, 17, 19, 25); | ||
int found = SortedListUtils.binarySearch(list, 29, Data::getValue, Integer::compare); | ||
assertEquals(-(list.size() + 1), found); | ||
} | ||
|
||
private List<Data> createList(int... values) { | ||
return IntStream.of(values).mapToObj(Data::new) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public class Data | ||
{ | ||
private final int value; | ||
|
||
public Data(int value) { | ||
this.value = value; | ||
} | ||
|
||
public int getValue() { | ||
return value; | ||
} | ||
} | ||
} |