关注牛哥公众号:牛牛码特,回复:1,即可获得秋招大礼包
面经统计表/Java Top100面试题/秋招企业投递表
为了解答这个问题,我们首先给一个来自 geoip 数据库的数据样例:
1.45.28.0 1.45.28.255 中国 北京 北京 * * 110000 39.938884 116.397459 Asia/Shanghai CN 45083 GTW 联通
1.45.29.0 1.45.43.255 中国 北京 北京 * * 110000 39.938884 116.397459 Asia/Shanghai CN 45083 IDC 联通
1.45.44.0 1.45.44.255 中国 北京 北京 * * 110000 39.938884 116.397459 Asia/Shanghai CN 45083 GTW 联通
其中第一行 1.45.28.0 1.45.28.255 中国 北京 北京
表示 1.45.28.0~1.45.28.255 范围内的 ip 归属于北京。
IPv4地址是32位二进制数,可直接转换为整数(0~2^32-1),便于范围比较。
例如:
1.45.28.0
转换为整数:1×256³ + 45×256² + 28×256 + 0 = 24485888
1.45.28.255
转换为整数:1×256³ + 45×256² + 28×256 + 255 = 24486143
转换后,每个IP段可表示为[start_int, end_int]
的整数范围,归属地信息(国家、城市等)与该范围绑定。
IP归属地查询的本质是"范围匹配",需判断目标IP是否落在某个IP段[start, end]
内,以下是两种主流实现方案:
(startInt, endInt, info)
,按startInt
从小到大排序(IP段无重叠是前提)。targetInt
;startInt ≤ targetInt
的IP段;endInt ≥ targetInt
是否成立,成立则返回对应信息。type IPLocation struct {
StartIP uint32 // 起始IP整数
EndIP uint32 // 结束IP整数
Location string // 归属地信息
}
func searchLocation(ipList []IPLocation, targetIP uint32) string {
// 使用sort.Search找到第一个StartIP大于targetIP的索引
idx := sort.Search(len(ipList), func(i int) bool {
return ipList[i].StartIP > targetIP
})
// 检查前一个IP段是否包含目标IP
if idx > 0 && ipList[idx-1].EndIP >= targetIP {
return ipList[idx-1].Location
}
return "未知归属地"
}
利用 Java 的 TreeMap
的floorKey
方法,键为IP段的startInt
,值为(endInt, info)
:
TreeMap<Long, IPDomain> treeMap = new TreeMap<>();
// 存储:key=startInt,value=包含endInt和归属地的对象
treeMap.put(24485888L, new IPDomain(24486143L, "中国北京..."));
// 查询:找到小于等于targetInt的最大startInt
Map.Entry<Long, IPDomain> entry = treeMap.floorEntry(targetInt);
if (entry != null && entry.getValue().endInt >= targetInt) {
return entry.getValue().info;
}
Go 标准库无TreeMap
,可使用第三方有序映射库(如github.com/emirpasic/gods
的BTree
或RedBlackTree
),逻辑与Java一致。