[英]How to binary-search on one field of a List's elements
Let C
be a class defined (partially) by 设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;
}
}
and let cs
be a List<C>
implementing RandomAccess
, and sorted on C.getX()
. 并且让cs
成为实现RandomAccess
的List<C>
,并在C.getX()
上排序。
What would be the standard approach to performing a binary search in cs
for x1
on C.getX()
? 什么是标准的做法,以在执行二进制搜索cs
为x1
上C.getX()
(In other words, pretending that each element c
is replaced by c.getX()
and then we search for x1
among those integers.) (换句话说,假装每个元素c
都被c.getX()
替换,然后我们在这些整数中搜索x1
。)
Collections.binarySearch(
cs,
new C(x1,/* dummy arguments */),
(a,b) -> Integer.compare(a.getX(),b.getX())
);
has the drawback that it requires construction of a new C
(which may require lots of dummy arguments and knowledge about C
). 它的缺点是它需要构造一个新的C
(可能需要大量的伪参数和关于C
知识)。
Collections.binarySearch(
cs.stream().map(C::getX).collect(Collectors.toList()),
x1
);
has the drawback that it creates an entire new list and is presumably O(n). 它的缺点是它创建了一个完整的新列表,大概是O(n)。
Is there a way to search in the stream directly, without collecting it? 有没有办法直接搜索流,而不收集它? Or perhaps some other way to search the original list without having to construct a dummy item? 或者也许还有其他方法可以搜索原始列表而无需构建虚拟项目?
In the absence of a better approach, I would do this: 如果没有更好的方法,我会这样做:
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();
}
}
and then 然后
Collections.binarySearch(
MappedView.of(cs, c -> c.getX()),
x1
);
Since you are in control of the Comparator
implementation, you can implement a symmetric comparison function that allows to compare instances of C
with values of the desired property, ie int
values (wrapped as an Integer
) in case of your x
property. 由于您可以控制Comparator
实现,因此可以实现对称比较函数,该函数允许将C
实例与所需属性的值进行比较,即在x
属性的情况下将int
值(包装为Integer
)进行比较。 It helps to know the factory methods comparing…
in the Comparator
interface which save you from repeating the code for both sides of the comparison: 它有助于了解comparing…
的工厂方法comparing…
,这样Comparator
重复比较两侧的代码:
int index = Collections.binarySearch(cs, x1,
Comparator.comparingInt(o -> o instanceof C? ((C)o).getX(): (Integer)o));
That way you can search for the int
value x1
without wrapping it in a C
instance. 这样,您可以搜索int
值x1
而不将其包装在C
实例中。
Another approach might be this: 另一种方法可能是这样的:
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()));
Or, if you don't mind a dependency, you can do this with Guava : 或者,如果您不介意依赖,可以使用Guava执行此操作:
List<Integer> xs = Lists.transform(cs, C::getX());
Collections.binarySearch(xs, x1);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.