刚来公司报道实习,主管交给我第一个任务是一个统计用户,的注册ip,区分是新疆,西藏,国外,国内其它地区。
之前有一个做完的工程,就直接给我了。我仔细阅读了一下,我先说一下思路,这个问题我之前电面阿里的时候,被问到过,现在才知道,也是艰难。
这是关键的几个资源txt,最后其实也就是靠着扫字典txt,来实现的。首先看一下,ip.txt的内容。
第一步就是要用buffered reader,来读取,这一整个文件,很大,每一行的第一个跟第三个是ip的前后范围。随之与之对应的是long(由ip转换),最后的的三个字段,第一个是代表城市code,第二个是省份code,后面的运营商。我们来看一下,city.txt,跟province.txt你就明白了。
下面province.txt
而fip是后来主管,给我的,但是发现是无规则的,而要求必须是由小到大的(原来后面就明白了)。
下面进入正题
First:加载ip.txt,跟fip.txt 分别加载成两个arraylist。ip对象到list,ip对象有一个startip,endip属性(数据源我当时是用的线上注册用户表,4000多万,累死,跑了接近两个小时)
Second: 加载province,city,用两个map来存放,便于后边根据,code来取值。
3:最关键的一步,我们获得数据源后,得到了ip字符串,我们怎么从ip从list找出来?答案就在startip,endip里,区间的二分查找,比较简单。所以我在第一步的时候,获得了fip(由于txt不是有序的)的list,我直接来了一部排序,看代码。
Comparator
public int compare(Ip s1, Ip s2) {
//先排年龄
if(s1.getEndIp()-s2.getEndIp()>0){
return 1;
}else{
return -1;
}
}};
Collections.sort(fips,comparator);
下面附上二分查找的代码
private Ip binarySearch(longipValue) {
Ip ip = null;
int size =ips.size();
int low = 0;
int high =size - 1;
while ((low <high) && (low <=size - 1) && (high <=size - 1)) {
int middle = (low +high) / 2;
if (ips.get(middle).getStartIp() ==ipValue) {
ip = ips.get(middle);
break;
} else if (ips.get(middle).getStartIp() >ipValue) {
if (ips.get(middle - 1).getStartIp() ==ipValue) {
ip = ips.get(middle - 1);
break;
} else if (ips.get(middle - 1).getStartIp() <ipValue) {
if (ipValue <=ips.get(middle - 1).getEndIp()) {
ip = ips.get(middle - 1);
break;
} else {
high = middle - 1;
}
} else {
high = middle - 1;
}
} else if (ips.get(middle).getStartIp() <ipValue) {
if (ips.get(middle + 1).getStartIp() >ipValue) {
if (ipValue <=ips.get(middle).getEndIp()) {
ip = ips.get(middle);
break;
} else {
high = middle - 1;
}
} else if (ips.get(middle + 1).getStartIp() ==ipValue) {
ip = ips.get(middle + 1);
break;
} else {
low = middle + 1;
}
}
}
// 位于第一个区间的IP
if ((low >=high) && (ipValue >=ips.get(0).getStartIp()) && (ipValue <=ips.get(0).getEndIp())) {
ip = ips.get(0);
}
// 位于最后一个区间的IP
if ((low >=high) && (ipValue >=ips.get(size - 1).getStartIp()) && (ipValue <=ips.get(size - 1).getEndIp())) {
ip = ips.get(size - 1);
}
return ip;
}
而基于经纬度定位,其实也就是扫另一个字典。