在实际开发中,我们经常会遇到需要在一个包含自定义对象的列表中查找特定元素的需求。例如,假设我们有一个Row对象列表,每个Row对象包含两个整型字段a和b:
class Row {
int a;
int b;
}已知该列表的特性是:如果按字段a排序,则字段b也会自动排序。我们的目标是编写一个函数find(int x, List
Java的Collections.binarySearch方法是解决此类问题的理想选择。它利用二分查找算法,能够在已排序的列表中以对数时间复杂度(O(log N))进行查找,远优于线性迭代(O(N))。
首先,我们需要完善Row类,使其包含构造函数、getter方法以及toString方法,以便于调试和输出。更重要的是,为了让Collections.binarySearch能够根据b字段进行查找,我们需要定义一个Comparator。
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
static class Row {
int a, b;
public int getA() { return a; }
public int getB() { return b; }
Row(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return "Row(" + a + ", " + b + ")";
}
}
// 定义一个基于b字段进行比较的比较器
static final Comparator ORDER_BY_B = Comparator.comparing(Row::getB);
Comparator.comparing(Row::getB)是一种简洁的Lambda表达式写法,用于创建一个根据Row对象的b字段进行升序比较的比较器。
核心的查找逻辑封装在find方法中。这个方法将接收一个目标整数x和一个Row对象列表rows。
static Row find(int x, Listrows) { int size = rows.size(); // 使用Collections.binarySearch进行查找 // 传入一个“虚拟”的Row对象作为查找键,其b值为x int i = Collections.binarySearch(rows, new Row(0, x), ORDER_BY_B); // 解析binarySearch的返回值来确定目标元素的索引 int index; if (i >= 0) { // 如果i >= 0,表示找到了精确匹配的元素,直接使用该索引 index = i; } else { // 如果i < 0,表示未找到精确匹配的元素。 // binarySearch返回的是 (-(插入点) - 1)。 // 插入点是该元素在列表中应插入的位置,即第一个大于它的元素的索引。 // 所以,-i - 1 就是这个插入点。 int insertionPoint = -i - 1; // 处理边界情况:如果x大于列表中所有b值,插入点将是列表大小 // 此时我们希望返回最后一个元素 if (insertionPoint >= size) { index = size - 1; } else { // 否则,插入点就是我们寻找的第一个大于或等于x的元素的索引 index = insertionPoint; } } // 返回找到的Row对象 return rows.get(index); }
binarySearch返回值解析:
索引计算逻辑解释:
我们的目标是找到第一个b值大于或等于x的Row。
nsertionPoint : 这意味着insertionPoint指向了列表中第一个b值大于x的元素。这就是我们所寻找的“大于或等于”的最近值。为了验证上述实现,我们可以创建一个main方法来测试find函数。
public static void main(String[] args) {
// 原始数据,注意它已经按a排序,并且b也随之排序
List rows = Arrays.asList(
new Row(20, 2),
new Row(40, 4),
new Row(50, 5),
new Row(70, 7));
// 确保列表是按b排序的,这是binarySearch的前提
// 虽然原始问题说按a排序b也排序,但为了严谨性,这里显式排序
List orderByB = rows.stream().sorted(ORDER_BY_B).collect(Collectors.toList());
System.out.println("Sorted list by B: " + orderByB);
// 测试不同x值
for (int i = 0; i < 9; ++i) {
System.out.println("find " + i + " : " + find(i, orderByB));
}
}
运行结果:
Sorted list by B: [Row(20, 2), Row(40, 4), Row(50, 5), Row(70, 7)] find 0 : Row(20, 2) find 1 : Row(20, 2) find 2 : Row(20, 2) find 3 : Row(40, 4) find 4 : Row(40, 4) find 5 : Row(50, 5) find 6 : Row(70, 7) find 7 : Row(70, 7) find 8 : Row(70, 7)
从输出可以看出,当x为0、1、2时,返回的是b值为2的Row(20, 2),因为它是第一个b值大于等于x的元素。当x为3时,返回Row(40, 4),以此类推。当x为8时,由于列表中没有b值大于等于8的元素,它返回了最后一个元素Row(70, 7),符合我们对“大于等于”且处理越界情况的预期。
通过以上方法,我们成功地利用Collections.binarySearch在Java中高效地解决了在自定义对象列表中查找特定字段“大于等于”的最近值的问题,为处理大数据量场景提供了可靠的解决方案。