如何二进制搜索List的元素的一个字段

时间:2015-10-12 17:37:47

标签: java collections binary-search java-stream

C成为(部分)由

定义的类
private static class C {
    private final int x;
    // lots more fields be here

    public C(int x, /* lots more arguments here */) {
        this.x = x;
        // lots more fields initialized here
    }

    public int getX(){
        return x;
    }
}

cs成为List<C>实施RandomAccess,并按C.getX()排序。

cs x1 C.getX()的{​​{1}}执行二进制搜索的标准方法是什么? (换句话说,假装每个元素cc.getX()替换,然后我们在这些整数中搜索x1。)

Collections.binarySearch(
    cs,
    new C(x1,/* dummy arguments */),
    (a,b) -> Integer.compare(a.getX(),b.getX())
);

的缺点是它需要构造一个新的C(这可能需要大量的伪参数和关于C的知识)。

Collections.binarySearch(
    cs.stream().map(C::getX).collect(Collectors.toList()),
    x1
);

的缺点是它创建了一个完整的新列表,大概是O(n)。

有没有办法直接搜索流,而不收集它?或者也许还有其他一些方法来搜索原始列表而不必构建虚拟项目?

如果没有更好的方法,我会这样做:

public class MappedView<T,E> extends AbstractList<E> {

    public static <T,E> MappedView<T,E> of(List<T> backingList, Function<T,E> f){
        return new MappedView<>(backingList, f);
    }

    private final List<T> backingList;
    private final Function<T,E> f;

    private MappedView(List<T> backingList, Function<T,E> f){
        this.f = f;
        this.backingList = backingList;
    }

    @Override
    public E get(int i) {
        return f.apply(backingList.get(i));
    }

    @Override
    public int size() {
        return backingList.size();
    }    
}

然后

Collections.binarySearch(
    MappedView.of(cs, c -> c.getX()),
    x1
);

2 个答案:

答案 0 :(得分:1)

由于您可以控制Comparator实现,因此可以实现对称比较函数,该函数允许将C的实例与所需属性的值进行比较,即int值(在Integer属性的情况下包装为x)。有助于了解comparing…中的工厂方法Comparator interface,这样可以避免重复比较双方的代码:

int index = Collections.binarySearch(cs, x1,
    Comparator.comparingInt(o -> o instanceof C? ((C)o).getX(): (Integer)o));

通过这种方式,您可以搜索intx1,而无需将其包含在C实例中。

答案 1 :(得分:0)

另一种方法可能是:

private static class C {
    private final int x;
    // lots more fields be here

    private C() {
    }

    public C(int x, /* lots more arguments here */) {
        this.x = x;
        // lots more fields initialized here
    }

    public int getX(){
        return x;
    }

    public static C viewForX(int x) {
        C viewInstance = new C();
        viewInstance.x = x;
        return viewInstance;
    }
}

Collections.binarySearch(cs, 
    C.viewForX(x1), 
    (a,b) -> Integer.compare(a.getX(),b.getX()));

或者,如果您不介意依赖,可以使用Guava

执行此操作
List<Integer> xs = Lists.transform(cs, C::getX());
Collections.binarySearch(xs, x1);