如何在集合中使用binarySearch?

时间:2016-06-15 20:52:37

标签: java collections binary-search

我有类动物与田地:重量和颜色。在这种情况下我如何使用 Collections.binarySearch (使用二进制搜索以所需大小查找某些动物):

 public static int searchElement(final List<? extends Animal> list, final int weight) {
    return Collections.binarySearch(list, weight...);
}

2 个答案:

答案 0 :(得分:2)

不幸的是,使用内置函数无法直接搜索基于某个属性的元素。

至少有三种选择可以解决这个问题:

  • 创建&#34;模板&#34;使用所需的属性,并搜索此
  • 将属性值提取到数组中,然后在此数组中搜索
  • 创建自己的基于属性的二进制搜索

第一个可能并不适用于所有情况,并且在某些方面看起来有问题。

第二个相当容易,可能是一个可行的选择。但假设您正在进行二进制搜索,因为该集合是 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,则转换后的列表也是如此。