【发布时间】:2020-11-22 08:01:48
【问题描述】:
问题
我有大量 IP 地址范围列表,我想有效地找到给定 IP 地址所在的范围。范围重叠是可能的。为了对 Stackoverflow 的这个问题进行简单和概括,我将 IP 地址替换为整数。 (但基本上,它可以是可以应用范围和范围排序的任何自定义类。)
问题示例
// Note: this class has a natural ordering that is inconsistent with equals.
class IntRange implements Comparable<IntRange> {
private int start;
private int end;
public IntRange(int start, int end) {
this.start = start;
this.end = end;
}
public boolean inRange(int i) {
return i >= start && i <= end;
}
@Override
public int compareTo(IntRange other) {
if (start < other.start) {
return -1;
} else if (start <= other.start && end >= other.end) {
return 0;
} else {
return 1;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IntRange intRange = (IntRange) o;
return start == intRange.start && end == intRange.end;
}
@Override
public int hashCode() {
return Objects.hash(start, end);
}
}
class Program {
private static List<IntRange> findRanges(IntRange[] ranges, int i) {
// How to implement this?
}
public static void main(String[] args) {
IntRange[] ranges = {
new IntRange(-10, 5),
new IntRange(8, 11),
new IntRange(9, 13),
new IntRange(20, 30),
new IntRange(800, 1000)
};
// Should contain IntRange(8, 12) and IntRange(9, 13) as result
List<IntRange> matchingRanges = findRanges(ranges,10);
}
}
鉴于上面的范围列表,我想找到包含给定整数的范围,例如 10。在这种情况下,只有范围 [8, 12] 会匹配,所以这就是结果。
问题
如果可能,如何使用 Java Collection API 解决这个问题? 该解决方案应该是有效的,因此通过列表进行暴力 N 搜索是不够有效的。
我也可以手动创建binary search tree,但我希望使用 Java 集合 API 使用比较器和 TreeSet 之类的东西,这样的事情应该是可能的?
通常,当使用 TreeSet 时,我会搜索相同类型的元素,例如,搜索 Person 对象,其中 firstname 和 lastname 必须匹配才能相等。但是在这种情况下,我想在 IntRanges 的 TreeSet 中搜索一个整数,所以不适合使用 equals 方法。
以 IP 地址代替整数的示例
可以为整数而不是 IP 地址提供解决方案,以保持问题的一般性和简单性。但是,如果您想尝试 IP 地址,是否可以使用此代码表示 IP 地址范围:
class IpRange {
private byte[] start; // 4 bytes for IPv4, 16 bytes for IPv6
private byte[] end;
// Only for testing purposes
public IpRange(int start, int end) {
this.start = BigInteger.valueOf(start).toByteArray();
this.end = BigInteger.valueOf(end).toByteArray();
}
public IpRange(byte[] start, byte[] end) {
this.start = start;
this.end = end;
}
public boolean inRange(byte[] ip) {
return Arrays.compare(start, ip) <= 0 && Arrays.compare(end, ip) >= 0;
}
public static void main(String[] args) {
// Test 1: test inRange function
IpRange ir = new IpRange(40, 60);
System.out.println(ir.inRange(BigInteger.valueOf(39).toByteArray())); // false
System.out.println(ir.inRange(BigInteger.valueOf(50).toByteArray())); // true
System.out.println(ir.inRange(BigInteger.valueOf(61).toByteArray())); // false
// Test 2
// In production, this range contains thousands of entries
IpRange[] ranges = {
new IpRange(-10, 5),
new IpRange(8, 12),
new IpRange(20, 30),
new IpRange(800, 1000)
};
// How to efficiently check in which ranges ip is 'inRange'?
int ip = 25;
}
}
【问题讨论】:
-
您可以将
java.util.Arrays.binarySearch与虚拟IpRange对象和自定义比较器一起使用 -
范围可以重叠还是保证不相交?
-
@joni 是的,它们可能会重叠,尽管这可能不会经常发生。
-
@user binarySearch 似乎只适用于匹配等于的对象。因此,如果我要创建一个像
new IntRange(10, 10)这样的假人,希望它与IntRange(8, 12)匹配,那么它就行不通了。 -
@user 我不确定为什么我尝试按照您的建议尝试
java.util.Arrays.binarySearch(),但没有成功。当@axelclk 在他的解决方案中尝试它时,它确实有效。所以也许我在尝试时做错了什么。但它现在可以工作了:)
标签: java collections range binary-search-tree binary-search