使用“Collections.binarySearch”签名实现二进制搜索
Implement binary search using the `Collections.binarySearch` signature
这是我尝试实现必须遵循的二进制搜索:
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
但是,我想避免代码重复并将一个实现委托给另一个实现(目前,第一个实现委托给第二个实现)。为此,我需要摆脱通配符 ?
并使用第二个泛型类型,如下所示:
public class BinarySearch {
public static <Q extends Comparable<? super T>, T>
int search(List<Q> xs, T x) {
return search(xs, x, Q::compareTo);
}
public static <T>
int search(List<? extends T> xs, T x, Comparator<? super T> cmp) {
int l = 0, r = xs.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (cmp.compare(xs.get(mid), x) == 0)
return mid;
if (cmp.compare(xs.get(mid), x) < 0)
r = mid - 1;
else
l = mid + 1;
}
return xs.size();
}
}
不幸的是,这没有编译,失败并出现错误:
Non-static method cannot be referenced from a static context
我该如何解决?
PS:如果您想知道为什么 Collections
的签名看起来是这样的,这里有一个解释:
PPS:曾经有一个(现已删除)答案,您不能在需要 Comparator
的地方传递 T::compareTo
。好吧,我相信你可以,这是我的 QuickSort 工作实现,它正是这样做的:https://github.com/all3fox/algos-java/blob/481f2c71952bf2c7510cb93cc1af8e90016ccf3b/src/main/java/io/github/all3fox/sort/QuickSort.java
其实我不明白为什么要用Q:
public static <T extends Comparable<T>>
int search(List<? extends T> xs, T x) {
return search(xs, x, T::compareTo);
}
将编译并且对我来说看起来足够了。
它允许我同时做:
BinarySearch.search(new ArrayList<Timestamp>(), new Date());
BinarySearch.search(new ArrayList<Date>(), new Timestamp(0L));
这里非常重要的一点是,这实际上意味着(或多或少):
int search(List<? extends T> xs, final T x) {
return search(xs, x, new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return x.compareTo(o2);
}
});
}
现在我们可以清楚地看到:x需要是Comparable类型。您的方法中没有说明这一点。相反,定义了一个类型 Q,但实际上没有参与者属于这种类型。因此 Comparator 并不严格兼容,但它必须兼容,因为 x 的 compareTo 方法是比较器的实现。顺便说一句,另一种方法是使用 Comparator.naturalOrder()
作为技巧,但仍然必须将 T 定义为 Comparable。
这是我尝试实现必须遵循的二进制搜索:
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
但是,我想避免代码重复并将一个实现委托给另一个实现(目前,第一个实现委托给第二个实现)。为此,我需要摆脱通配符 ?
并使用第二个泛型类型,如下所示:
public class BinarySearch {
public static <Q extends Comparable<? super T>, T>
int search(List<Q> xs, T x) {
return search(xs, x, Q::compareTo);
}
public static <T>
int search(List<? extends T> xs, T x, Comparator<? super T> cmp) {
int l = 0, r = xs.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (cmp.compare(xs.get(mid), x) == 0)
return mid;
if (cmp.compare(xs.get(mid), x) < 0)
r = mid - 1;
else
l = mid + 1;
}
return xs.size();
}
}
不幸的是,这没有编译,失败并出现错误:
Non-static method cannot be referenced from a static context
我该如何解决?
PS:如果您想知道为什么 Collections
的签名看起来是这样的,这里有一个解释:
PPS:曾经有一个(现已删除)答案,您不能在需要 Comparator
的地方传递 T::compareTo
。好吧,我相信你可以,这是我的 QuickSort 工作实现,它正是这样做的:https://github.com/all3fox/algos-java/blob/481f2c71952bf2c7510cb93cc1af8e90016ccf3b/src/main/java/io/github/all3fox/sort/QuickSort.java
其实我不明白为什么要用Q:
public static <T extends Comparable<T>>
int search(List<? extends T> xs, T x) {
return search(xs, x, T::compareTo);
}
将编译并且对我来说看起来足够了。 它允许我同时做:
BinarySearch.search(new ArrayList<Timestamp>(), new Date());
BinarySearch.search(new ArrayList<Date>(), new Timestamp(0L));
这里非常重要的一点是,这实际上意味着(或多或少):
int search(List<? extends T> xs, final T x) {
return search(xs, x, new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return x.compareTo(o2);
}
});
}
现在我们可以清楚地看到:x需要是Comparable类型。您的方法中没有说明这一点。相反,定义了一个类型 Q,但实际上没有参与者属于这种类型。因此 Comparator 并不严格兼容,但它必须兼容,因为 x 的 compareTo 方法是比较器的实现。顺便说一句,另一种方法是使用 Comparator.naturalOrder()
作为技巧,但仍然必须将 T 定义为 Comparable。