简体   繁体   English

如何在集合中使用binarySearch?

[英]How to use binarySearch in Collections?

l'm have class Animal with field: weight and color. 我有动物类,其中包括:重量和颜色。 How I can used Collections.binarySearch in this case (use binary search to find some animal by required size): 在这种情况下,如何使用Collections.binarySearch (使用二进制搜索按所需大小查找某些动物):

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

Unfortunately, it is not directly possible to search for an element based on a certain property, using the built-in functions. 不幸的是,使用内置函数无法直接基于某个属性搜索元素。

There are at least three options how this could be solved: 至少有三个选项可以解决此问题:

  • Creating a "template" with the desired property, and search for this 创建具有所需属性的“模板”,然后搜索
  • Extract the property values into an array, and search in this array 将属性值提取到数组中,然后在该数组中搜索
  • Create an own, property-based binary search 创建自己的基于属性的二进制搜索

The first one may not be applicable in all cases, and looks questionable in some ways. 第一个可能并不适用于所有情况,并且在某些方面看起来值得怀疑。

The second one is rather easy and could be a viable option. 第二个很简单,可能是一个可行的选择。 But assuming that you are doing a binary search because the collection is large , this may impose some overhead in terms of memory and performance. 但是假设由于集合很大而正在执行二进制搜索,则可能在内存和性能方面带来一些开销。

The third option is probably the most elegant and versatile one. 第三种选择可能是最优雅,最通用的一种。 Fortunately, the binarySearch itself is not so complex - only a few lines of code - so it's easy to craft an own one that receives some "key extracting Function ". 幸运的是, binarySearch本身并不那么复杂-仅需几行代码-因此,很容易制作出自己的可以接收“密钥提取Function ”的代码。

I have sketched these approaches in the following example: 我在以下示例中概述了这些方法:

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
    }

}

You can lazily transform the list into a list of the type you want: 您可以将列表懒惰地转换为所需类型的列表:

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);

The transform is lazy, in that it will only convert the values that are being compared. 转换是惰性的,因为它将仅转换要比较的值。


Or, if you can use Guava's Lists.transform : 或者,如果您可以使用番石榴的Lists.transform

Collections.binarySearch(Lists.transform(animals, Animal::weight), searchWeight);

And yes, if the input list is RandomAccess , so is the transformed list. 是的,如果输入列表是RandomAccess ,则转换后的列表也是如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM