我有类动物与田地:重量和颜色。在这种情况下我如何使用 Collections.binarySearch (使用二进制搜索以所需大小查找某些动物):
public static int searchElement(final List<? extends Animal> list, final int weight) {
return Collections.binarySearch(list, weight...);
}
答案 0 :(得分:2)
不幸的是,使用内置函数无法直接搜索基于某个属性的元素。
至少有三种选择可以解决这个问题:
第一个可能并不适用于所有情况,并且在某些方面看起来有问题。
第二个相当容易,可能是一个可行的选择。但假设您正在进行二进制搜索,因为该集合是 large ,这可能会在内存和性能方面产生一些开销。
第三种选择可能是最优雅和多才多艺的选择。幸运的是,binarySearch
本身并不是那么复杂 - 只有几行代码 - 因此很容易制作一个自己的代码来接收一些&#34;密钥提取Function
&#34 ;
我已在以下示例中概述了这些方法:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
class Animal implements Comparable<Animal>
{
private final int weight;
Animal(int weight)
{
this.weight = weight;
}
public int getWeight()
{
return weight;
}
@Override
public int compareTo(Animal that)
{
return Integer.compare(this.weight, that.weight);
}
}
public class CollectionBinarySearch
{
public static void main(String[] args)
{
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal(10));
animals.add(new Animal(40));
animals.add(new Animal(20));
animals.add(new Animal(90));
animals.add(new Animal(290));
animals.add(new Animal(130));
Collections.sort(animals);
System.out.println(searchWithInstance(animals, 90));
System.out.println(searchWithInstance(animals, 50));
System.out.println(searchWithArray(animals, 90));
System.out.println(searchWithArray(animals, 50));
System.out.println(searchWithFunction(animals, Animal::getWeight, 90));
System.out.println(searchWithFunction(animals, Animal::getWeight, 50));
}
public static int searchWithInstance(
final List<? extends Animal> list, final int weight) {
return Collections.binarySearch(list, new Animal(weight));
}
public static int searchWithArray(
final List<? extends Animal> list, final int weight) {
int[] array = list.stream().mapToInt(Animal::getWeight).toArray();
return Arrays.binarySearch(array, weight);
}
// Adapted from Collections#binarySearch
private static <T, K extends Comparable<? super K>> int searchWithFunction(
List<? extends T> list, Function<? super T, K> keyExtractor, K key) {
int low = 0;
int high = list.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1;
T midVal = list.get(mid);
int cmp = keyExtractor.apply(midVal).compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
}
答案 1 :(得分:0)
您可以将列表懒惰地转换为所需类型的列表:
In [2]: ["\n".join(['joe','john','jane'])]
Out[2]: ['joe\njohn\njane']
转换是惰性的,因为它只会转换要比较的值。
或者,如果您可以使用番石榴的Lists.transform
:
class LazyTransform extends AbstractList<Integer> implements RandomAccess {
@Override public Integer get(int index) { return items.get(index).weight(); }
@Override public int size() { return items.size(); }
}
Collections.binarySearch(new LazyTransform(), searchWeight);
是的,如果输入列表为RandomAccess
,则转换后的列表也是如此。