IP归属地在线查询平台 javaSE
1.项目介绍
1.背景
根据IP得到位置,加标签
进行大数据分析,比如太白推荐等提供优质数据
www.ip.cn 等 查询ip
2.需求
IP分析 归属地信息,查找在毫秒内完成
IP地址库 公网都是开放的
IANA:国际组织,负责公网IP维护分发
3.技术栈
Eclipse,javaSE中面向对象/IO流/二分法算法/Base64编码/工具类封装
4.目标
通过开发IP地质归属地查询平台,我们需要对javaSE综合技术有所提升,增强实战能力.学习完该项目我们应该具备以下能力:
1 面向对象程序设计
2 工具类封装与使用写法
3 文件IO流
4 字符串处理
5 二分法查找
6 IP 地址的不同形式的使用
2.相关问题
强化项目四位
不管好方法还是差方法,先尝试把功能实现再考虑对代码进行优化,最优位置
3.主要思路
1 解析提供的地址库字符串,为结构化数据格式
2 基于结构化数据构建成某个数据结构,加速给定IP地址的查找速度
3 封装成响应的工具类API,开放其响应的方法,即给定IP地址可以在ms内计算得到其位置信息
4 工具类只有一个入参一个出参
入参是IP 出参是地址
4.研发项目分类
1 应用开发类项目
解决某些特定功能而开发的应用软件,比如QQ,微信,Eclipse等
2 Web开发类项目
以B/S架构为主,也就是网页形式访问的在线系统,如各类官网等
3 中小型应用类开发项目的流程
1 需求概述:需求描述,说清楚你要干什么,为什么做这个
在互联网公司中,根据IP地址获取归属地信息是非常广泛的,开发的这个项目能解决这个问题
2 需求分析: 需要根据需求概述,用技术角度来看一下这个项目是否可行
可行性一定是可以做的
需求分析的梳理:
三方面:输入,输出,已具备的物料(前置条件)
输入:给定任意一个IP地址
输出:返回IP对应的归属地
前置条件:
IP地址库
javaSE
面向对象
IO
常用类
二分法
正则表达式校验
3 开发步骤拆分,可以理解为解耦,把一个拆分为多个
1 读取IP地址库
2 解析IP地址每行数据,进行结构化 找到无结构化数据的规则,进行结构化处理 简单来说就是根据需求生成实体类
Entity/model:实体类,一般该类和数据库表是一一对应的
DTO:先不管
Pojo:无特殊意义,纯粹的业务对象
3 把对象加入List中
4 转换为数组,方便二分法操作 为什么不直接转换为数组?数组长度问题,不清楚有多少行,有多少空行等 ArrayList转数组,不会消耗很多时间,因为底层就是数组
5 解决二分法查找的技术问题
6 完成比较逻辑
7 对外提供访问接口
8 测试
4 细节开发与风险控制
5 BUG修复,调优,标准化
6 正式上线
7 项目总结,项目复盘
5.代码研发
- 无脑读取文件
在test文件夹下 创建 TestFileIO_01.java
package com;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* 1 定义文件路径
*
* 2 通过节点流对接到文本上
*
* 3 将节点流转换为字符流
*
* 4 通过缓冲流对接到输入流
*
* 5 读取
*
* 6 关闭流
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:06:32
*/
public class TestFileIO_01 {
public static void main(String[] args) {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
try (
// 字节流
FileInputStream fis = new FileInputStream(ipLibrartPath);
// 转换为字符流
Reader reader = new InputStreamReader(fis, encoding);
// 封装缓冲流
BufferedReader br = new BufferedReader(reader);) {
String temp = null;
while ((temp = br.readLine()) != null) {
System.out.println(temp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 文本文件读取工具类
工具类封装 :
1 先写测试类,确认输入与输出技术问题
2 抽象出了输入与输出,形成方法的入参和出参
3 工具代码实现,测试
- 工具类
package com.tledu.zrz.util;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
/**
* 读取文件
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:17:26
*/
public class FileOperatorUtil {
/**
* 读取文件,并返回List
*
* @param txtFilePath
* 字符串格式的文件路径
* @param encoding
* 字符编码
* @return
* @throws IOException
*/
public static List<String> getLineList(String txtFilePath, String encoding)
throws IOException {
List<String> lineLine = new ArrayList<String>();
// 字节流
FileInputStream fis = new FileInputStream(txtFilePath);
// 转换为字符流
Reader reader = new InputStreamReader(fis, encoding);
// 封装缓冲流
BufferedReader br = new BufferedReader(reader);
String temp = null;
while ((temp = br.readLine()) != null) {
lineLine.add(temp);
}
// 关闭资源
br.close();
return lineLine;
}
}
- 测试工具类
package com;
import java.io.IOException;
import java.util.List;
import com.tledu.zrz.util.FileOperatorUtil;
/**
* 测试工具类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:06:32
*/
public class TestFileIO_02 {
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
for (String string : lineList) {
System.out.println(string);
}
}
}
- 数据结构化
1 可以找无结构化数据的组织规则,进行抽象,转换为结构化
2 根据数据组织进行数据抽象
3 解析数据,保存到对应的对象中
Model,pojo,dto
- 抽象pojo类
- 抽象封装
package com.tledu.zrz.pojo;
/**
* 结构化实体类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:29:50
*/
public class IPAndLocationPojo {
@Override
public String toString() {
return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP
+ ", location=" + location + "]";
}
/**
* 起始IP
*/
private String startIP;
/**
* 结束IP
*/
private String endIP;
/**
* 归属地
*/
private String location;
public String getStartIP() {
return startIP;
}
public void setStartIP(String startIP) {
this.startIP = startIP;
}
public String getEndIP() {
return endIP;
}
public void setEndIP(String endIP) {
this.endIP = endIP;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public IPAndLocationPojo(String startIP, String endIP, String location) {
super();
this.startIP = startIP;
this.endIP = endIP;
this.location = location;
}
public IPAndLocationPojo() {
super();
}
}
- 数据拆分,转换为对象
package com;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
/**
* 转换为结构化对象
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:32:39
*/
public class TestNonStructToStruct_01 {
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以\t分割,得到三个列
String[] columnArray = string.split("\t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
// 遍历集合
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojos) {
System.out.println(ipAndLocationPojo);
}
}
}
- 封装DataProcessManager类
- 把数据封装到对象中并保存list里面
package com.tledu.zrz.manager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 该类为项目中管理类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:45:19
*/
public class DataProcessManager {
/**
* 把读取到的List<String> 转换为 List<IPAndLocationPojo>
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
public static List<IPAndLocationPojo> getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以\t分割,得到三个列
String[] columnArray = string.split("\t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
- 测试
package com;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.tledu.zrz.manager.DataProcessManager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
/**
* 转换为结构化对象
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:32:39
*/
public class TestNonStructToStruct_02 {
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath, encoding);
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojos) {
System.err.println(ipAndLocationPojo);
}
}
}
- 结构化集合转换为数组
package com;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.tledu.zrz.manager.DataProcessManager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
public class TestListToArray_01 {
public static void main(String[] args) throws IOException {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
String[] arr = new String[list.size()];
list.toArray(arr);
for (String string : arr) {
System.out.println(string);
}
// 业务数据
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrartPath, encoding);
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[ipAndLocationPojos
.size()];
ipAndLocationPojos.toArray(ipLocationPojoArr);
for (IPAndLocationPojo ipAndLocationPojo : ipLocationPojoArr) {
System.out.println(ipAndLocationPojo);
}
}
}
- 对象数组排序
- 实现排序
1 自定义实现
冒泡,选择
2 API
Collections,Arrays
- 排序注意事项
1 被排序的元素必须具有可比性
实现Comparable接口
有比较器类Comparator
- 排序案例
package com;
import java.util.Arrays;
public class TestArrySort_01 {
public static void main(String[] args) {
// 数字 : 从小到大
// 字符串 : ASCII码
// 日期 : 自然日期
// 因为 Integer,String,Date 都实现了Comparable接口
int[] intArray = { 1, 13, 24, 7, 4 };
Arrays.sort(intArray);
for (int i : intArray) {
System.out.println(i);
}
A a1 = new A(18, "张三1");
A a2 = new A(19, "张三2");
A a3 = new A(15, "张三3");
A a4 = new A(12, "张三4");
A[] a = { a1, a2, a3, a4 };
Arrays.sort(a);
for (A a5 : a) {
System.out.println(a5);
}
}
}
class A implements Comparable<A>{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public A(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "A [age=" + age + ", name=" + name + "]";
}
@Override
public int compareTo(A o) {
return age-o.age;
}
}
- 总结
要么实现排序的类 实现Comparable接口
- 业务问题
由于IP是字符串,而字符串比较ASCII码
比如 1.1.6.32 , 1.1.128.23 理论上应该是后面的大
但是按照ASCII码比较的话,前面的大
所以我们需要把IP转换为long类型进行比较即可
package com.tledu.zrz.util;
/**
* IP和long类型之间的转换
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 下午2:51:10
*/
public class IPUtil {
public static void main(String[] args) {
long x = ipToLong("120.211.144.131");
System.out.println(x);
String s = longToIp(x);
System.out.println(s);
}
private static Long ipToLong(String ipStr) {
long ipLong = 0;
if (ipStr != null && ipStr.length() > 0) {
// 将ip(点分十进制的形式 a.b.c.d) 地址按.分割
String[] ipSplit = ipStr.split("\\.");
try {
if (ipSplit.length != 4) {
throw new Exception("IP Format Error");
}
for (int i = 0; i < ipSplit.length; i++) {
int temp = Integer.parseInt(ipSplit[i]);
ipLong += temp * (1L << (ipSplit.length - i - 1) * 8);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
}
return ipLong;
}
/**
* 将long型IP地址转换回点分十进制表示的字符串类型
*
* @param ipLong
* @return java.lang.String
* @throws Exception
*/
private static String longToIp(long ipLong) {
StringBuffer ipStr = new StringBuffer();
try {
if (ipLong < 0) {
throw new Exception("Can not to IP...");
}
// 最高8位,直接右移24位
ipStr.append((ipLong >>> 24));
ipStr.append(".");
// 将高8位设置0,然后右移16位
ipStr.append(((ipLong & 0x00FFFFFF) >>> 16));// 获得高8位,6个f对应的是24个1,最高8位设置空为0,之后右移16位将前面多余的16位去掉,以下类推即可
ipStr.append(".");
// 将高16位设置0,然后右移8位
ipStr.append((ipLong & 0x0000FFFF) >>> 8); // 前16位
// 设置0,移除低8位,16个1,高16位设置为0
ipStr.append(".");
// 将高24位设置0
ipStr.append(ipLong & 0x000000FF); // 前24位 设置0,留低8位,8个1,高24位设置为0
} catch (Exception e) {
e.printStackTrace();
}
return ipStr.toString();
}
}
- Pojo添加衍生字段
实体类中衍生两个字段,用来存储转换为long类型之后的值
package com.tledu.zrz.pojo;
import com.tledu.zrz.util.IPUtil;
/**
* 结构化实体类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:29:50
*/
public class IPAndLocationPojo implements Comparable<IPAndLocationPojo> {
@Override
public String toString() {
return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP
+ ", location=" + location + "]";
}
/**
* 起始IP
*/
private String startIP;
// 衍生字段
private long startIPLong;
private long endIPLong;
/**
* 结束IP
*/
private String endIP;
public long getStartIPLong() {
return startIPLong;
}
public void setStartIPLong(long startIPLong) {
this.startIPLong = startIPLong;
}
public long getEndIPLong() {
return endIPLong;
}
public void setEndIPLong(long endIPLong) {
this.endIPLong = endIPLong;
}
/**
* 归属地
*/
private String location;
public String getStartIP() {
return startIP;
}
public void setStartIP(String startIP) {
this.startIP = startIP;
}
public String getEndIP() {
return endIP;
}
public void setEndIP(String endIP) {
this.endIP = endIP;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public IPAndLocationPojo(String startIP, String endIP, String location) {
super();
this.startIP = startIP;
this.endIP = endIP;
this.location = location;
this.startIPLong = IPUtil.ipToLong(startIP);
this.endIPLong = IPUtil.ipToLong(endIP);
}
public IPAndLocationPojo() {
super();
}
@Override
public int compareTo(IPAndLocationPojo o) {
// 因为IP段没有交集,所以使用起始和结束 比较 都是可以的
long result = startIPLong - o.startIPLong;
if (result > 0) {
return 1;
} else if (result < 0) {
return -1;
} else {
return 0;
}
}
}
- 测试Pojo排序
package com;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import com.tledu.zrz.manager.DataProcessManager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
public class TestArrySort_02 {
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrartPath, encoding);
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[ipAndLocationPojos
.size()];
ipAndLocationPojos.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
// 测试首位元素
System.out.println(ipLocationPojoArr[0]);
System.out.println(ipLocationPojoArr[ipLocationPojoArr.length-1]);
}
}
- 封装排序
package com.tledu.zrz.manager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 该类为项目中管理类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:45:19
*/
public class DataProcessManager {
/**
* 将对象集合转换为对象数组并排序
*
* @param pojoList
* @return
*/
public static IPAndLocationPojo[] convertListToArrayAndSort(
List<IPAndLocationPojo> pojoList) {
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList
.size()];
pojoList.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
return ipLocationPojoArr;
}
/**
* 把读取到的List<String> 转换为 List<IPAndLocationPojo>
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
public static List<IPAndLocationPojo> getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以\t分割,得到三个列
String[] columnArray = string.split("\t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
- 测试
package com;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import com.tledu.zrz.manager.DataProcessManager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
public class TestArrySort_03 {
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrartPath, encoding);
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager.convertListToArrayAndSort(ipAndLocationPojos);
// 测试首位元素
System.out.println(ipLocationPojoArr[0]);
System.out.println(ipLocationPojoArr[ipLocationPojoArr.length-1]);
}
}
- 二分法
二分法查询,必须建立在已排序的基础上
- 基本类型
public static void main(String[] args) {
// 基础类型
int[] intArray = {1,3,5,7,9,11,100,900};
int aid = 9200;
int startIndex = 0;
int endIndex = intArray.length-1;
int m = (endIndex+startIndex)/2;
while (startIndex <= endIndex) {
if (aid == intArray[m]) {
System.out.println(m);
return;
}else if (aid > intArray[m]) {
startIndex = m+1;
}else{
endIndex = m -1;
}
m=(startIndex+endIndex)/2;
}
System.out.println("没找着");
}
- 引用/复杂类型
public static void main(String[] args) {
User u1 = new User(18);
User u2 = new User(19);
User u3 = new User(20);
User u4 = new User(21);
User u5 = new User(22);
// 复杂类型
User[] intArray = { u1, u2, u3, u4, u5 };
int aid = 23;
int startIndex = 0;
int endIndex = intArray.length - 1;
int m = (endIndex + startIndex) / 2;
while (startIndex <= endIndex) {
if (aid == intArray[m].getAge()) {
System.out.println(m);
return;
} else if (aid > intArray[m].getAge()) {
startIndex = m + 1;
} else {
endIndex = m - 1;
}
m = (startIndex + endIndex) / 2;
}
System.out.println("没找着");
}
- 业务类实现
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrartPath, encoding);
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager
.convertListToArrayAndSort(ipAndLocationPojos);
// 用户输入的IP
// String aid = "125.152.1.22";
String aid = "120.211.144.131";
// 转换为long类型
long ipLong = IPUtil.ipToLong(aid);
int startIndex = 0;
int endIndex = ipLocationPojoArr.length - 1;
int m = (endIndex + startIndex) / 2;
while (startIndex <= endIndex) {
// 大于等于起始 小于等于结束 说明找到了
// 小于起始 在前半截
// 大于结束 在后半截
if (ipLong > ipLocationPojoArr[m].getEndIPLong()) {
startIndex = m + 1;
} else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) {
endIndex = m - 1;
} else {
System.out.println(ipLocationPojoArr[m]);
return;
}
m = (startIndex + endIndex) / 2;
}
System.out.println("没找着");
}
- IP地址对象的范围形式数据的二分法实现
- 业务类功能实现
package com.tledu.zrz.manager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
import com.tledu.zrz.util.IPUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 该类为项目中管理类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:45:19
*/
public class DataProcessManager {
/**
* 业务类对应的二分法
*
* @param ipLocationPojoArr
* 数组
* @param ip
* ip
* @return 索引
*/
public static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr,
String ip) {
// 转换为long类型
long ipLong = IPUtil.ipToLong(ip);
int startIndex = 0;
int endIndex = ipLocationPojoArr.length - 1;
int m = (endIndex + startIndex) / 2;
while (startIndex <= endIndex) {
// 大于等于起始 小于等于结束 说明找到了
// 小于起始 在前半截
// 大于结束 在后半截
if (ipLong > ipLocationPojoArr[m].getEndIPLong()) {
startIndex = m + 1;
} else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) {
endIndex = m - 1;
} else {
return m;
}
m = (startIndex + endIndex) / 2;
}
return -1;
}
/**
* 将对象集合转换为对象数组并排序
*
* @param pojoList
* @return
*/
public static IPAndLocationPojo[] convertListToArrayAndSort(
List<IPAndLocationPojo> pojoList) {
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList
.size()];
pojoList.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
return ipLocationPojoArr;
}
/**
* 把读取到的List<String> 转换为 List<IPAndLocationPojo>
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
public static List<IPAndLocationPojo> getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以\t分割,得到三个列
String[] columnArray = string.split("\t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
- 测试
package com;
import java.io.IOException;
import java.util.List;
import com.tledu.zrz.manager.DataProcessManager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.IPUtil;
public class TestBinaraySearch_02 {
public static void main(String[] args) throws IOException {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrartPath, encoding);
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager
.convertListToArrayAndSort(ipAndLocationPojos);
// 用户输入的IP
String ip = "125.152.1.22";
// String ip = "120.211.144.131";
int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip);
System.out.println(ipLocationPojoArr[index]);
}
}
- 工具类封装
到目前为止,已经把二分法搞定了,已经可以实现功能了,测试代码就相当于客户端
但是目前客户端知道的信息还是比较多,客户只关心 入参和出参是什么就可以
至于我们用什么编码,用什么存储,客户根本不关心
此时我们需要提供一个对外访问的方法,该方法入参就是IP 出参就是归属地
- 编码
package com.tledu.zrz.manager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
import com.tledu.zrz.util.IPUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 该类为项目中管理类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:45:19
*/
public class DataProcessManager {
/**
* 对外提供的接口,入参IP出参地址
*
* @param ip
* @return
*/
public static String getLocation(String ip) {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List<IPAndLocationPojo> ipAndLocationPojos = null;
try {
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath,
encoding);
} catch (IOException e) {
e.printStackTrace();
}
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager
.convertListToArrayAndSort(ipAndLocationPojos);
int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip);
return ipLocationPojoArr[index].getLocation();
}
/**
* 业务类对应的二分法
*
* @param ipLocationPojoArr
* 数组
* @param ip
* ip
* @return 索引
*/
private static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr,
String ip) {
// 转换为long类型
long ipLong = IPUtil.ipToLong(ip);
int startIndex = 0;
int endIndex = ipLocationPojoArr.length - 1;
int m = (endIndex + startIndex) / 2;
while (startIndex <= endIndex) {
// 大于等于起始 小于等于结束 说明找到了
// 小于起始 在前半截
// 大于结束 在后半截
if (ipLong > ipLocationPojoArr[m].getEndIPLong()) {
startIndex = m + 1;
} else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) {
endIndex = m - 1;
} else {
return m;
}
m = (startIndex + endIndex) / 2;
}
return -1;
}
/**
* 将对象集合转换为对象数组并排序
*
* @param pojoList
* @return
*/
private static IPAndLocationPojo[] convertListToArrayAndSort(
List<IPAndLocationPojo> pojoList) {
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList
.size()];
pojoList.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
return ipLocationPojoArr;
}
/**
* 把读取到的List<String> 转换为 List<IPAndLocationPojo>
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
private static List<IPAndLocationPojo> getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以\t分割,得到三个列
String[] columnArray = string.split("\t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
- 测试
package com;
import java.io.IOException;
import com.tledu.zrz.manager.DataProcessManager;
public class TestBinaraySearch_03 {
public static void main(String[] args) throws IOException {
// String ip = "125.152.1.22";
String ip = "120.211.144.131";
String location = DataProcessManager.getLocation(ip);
System.out.println(location);
}
}
- 优化
功能实现了,像以下情况,同一个生命周期中,如果操作两次及两次以上
那么 会导致 ip_location_relation地址库被解析多次,并且结构化多次,并且排序多次
带来的性能变弱
因为在一个生命周期当前,只解析一次,结构化一次,排序一次即可,每次获取归属地的时候,只需要进行二分法操作即可
可以使用静态代码块解决
package com.tledu.zrz.manager;
import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
import com.tledu.zrz.util.IPUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 该类为项目中管理类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 上午11:45:19
*/
public class DataProcessManager {
private static IPAndLocationPojo[] ipLocationPojoArr = null;
static {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List<IPAndLocationPojo> ipAndLocationPojos = null;
try {
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath,
encoding);
} catch (IOException e) {
e.printStackTrace();
}
// 转换为数组
ipLocationPojoArr = DataProcessManager
.convertListToArrayAndSort(ipAndLocationPojos);
}
/**
* 对外提供的接口,入参IP出参地址
*
* @param ip
* @return
*/
public static String getLocation(String ip) {
int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip);
return ipLocationPojoArr[index].getLocation();
}
/**
* 业务类对应的二分法
*
* @param ipLocationPojoArr
* 数组
* @param ip
* ip
* @return 索引
*/
public static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr,
String ip) {
// 转换为long类型
long ipLong = IPUtil.ipToLong(ip);
int startIndex = 0;
int endIndex = ipLocationPojoArr.length - 1;
int m = (endIndex + startIndex) / 2;
while (startIndex <= endIndex) {
// 大于等于起始 小于等于结束 说明找到了
// 小于起始 在前半截
// 大于结束 在后半截
if (ipLong > ipLocationPojoArr[m].getEndIPLong()) {
startIndex = m + 1;
} else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) {
endIndex = m - 1;
} else {
return m;
}
m = (startIndex + endIndex) / 2;
}
return -1;
}
/**
* 将对象集合转换为对象数组并排序
*
* @param pojoList
* @return
*/
public static IPAndLocationPojo[] convertListToArrayAndSort(
List<IPAndLocationPojo> pojoList) {
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList
.size()];
pojoList.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
return ipLocationPojoArr;
}
/**
* 把读取到的List<String> 转换为 List<IPAndLocationPojo>
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
public static List<IPAndLocationPojo> getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以\t分割,得到三个列
String[] columnArray = string.split("\t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
- 测试
- 入口类
package com.tledu.zrz.controller;
import java.util.Scanner;
import com.tledu.zrz.manager.DataProcessManager;
/**
* 程序入口
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 下午4:41:42
*/
public class SystemController {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入IP : ");
String ip = scanner.nextLine();
long startTime = System.currentTimeMillis();
String location = DataProcessManager.getLocation(ip);
long endTime = System.currentTimeMillis();
System.out.println(location + " : " + (endTime - startTime));
}
}
}
IP归属地在线查询平台 javaSE相关推荐
- IP地址归属地在线查询平台
一.项目介绍 1.背景 根据IP得到位置,加标签 进行大数据分析,比如淘宝推荐等提供优质数据 www.ip.cn 等 查询IP 2.需求 IP 分析 归属地信息 , 查找在毫秒内完成 IP地址库,公网 ...
- 深圳市补贴政策在线查询平台,2021年最新深圳企业补贴项目
在企业日常经营发展过程中,企业通过申请政府补贴项目可以获得一定程度上的资金支持,有利于缓解企业的资金压力与品牌塑造.而在这一过程中,我们需要找到一个合适的深圳市补贴政策在线查询平台,从而查询到2021 ...
- 2023基于微信小程序的公交信息在线查询平台(SSM+mysql)-JAVA.VUE(论文+开题报告+运行)
摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟.本文介绍了微信小程序公交信息在线查询系统的开发全过程.通过分析微信小程序公交信息在线查询系统信息管理的不足,创建 ...
- IP归属地批量查询软件
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前言: 前段时间有个需求是把几万个IP解析为国家,省份,城市,运营商等形式,本来想写个爬虫不断去向网站post,但想了想,这样慢不说, ...
- Excel也可以根据ip地址在线查询归属地详细信息
今天要和大家分享的是:Excel也可以根据ip地址在线查询归属地详细信息,先看下面的动图演示 1.先装好该插件,然后打开表格 2.然后选择ip地址,将获得国家.大区.省.市.县.运营商等详细信 ...
- 案例—手机号码归属地专业在线查询
使用Ajax异步请求向服务器发送GET请求,请求手机号码归属地查询,然后动态生成列表,显示手机号码段,卡号归属地,卡类型,区号,邮编等列表信息,最后写一个超链接把更多详情的邮区编号写进去. 本案例整体 ...
- 最新社工库在线查询网站
社工查询网站 手机号注册网站查询 信用查询 国内企业信息 政府信息查询 身份信息查询 驾驶员及车辆信息查询 物品资产查询 物流查询 发票查询 金融查询 手机信息查询 个人信息查询 搜索引擎 手机号注册 ...
- Java IP归属地查询(离线方式+在线方式,内附查询IP方法)
一.离线方式 1.1. 下载 ip2region.xdb GitHub项目地址:https://github.com/lionsoul2014/ip2region 我们首先需要下载一个 ip2regi ...
- IP归属地查询(基于本地IP库实现)
这是一篇GitHub上的项目说明文档,项目地址: Zzz2333/IPLibrary 本项目功能是根据IP查询归属地.用到了纯真IP地址数据库,即qqwry.dat文件. 纯真IP地址数据库是一个网上 ...
最新文章
- 【小样本学习】什么是小样本学习?这篇综述文章用166篇参考文献告诉你答案...
- 如何检查字符串是否为空?
- 集训队脱单大法:这是一道只能由学姐我自己出数据的水题
- .net core mvc初级教程(六)
- 电脑怎么结束进程_小协漫谈 | 聊聊电脑卡顿那些事
- TensorFlow 教程 --新手入门--1.1简介
- 打印异常堆栈_通过异常堆栈丢失谈即时编译优化
- IOS7 position:fixed focus定位问题
- 开启html元素的编辑模式contenteditable=true
- Appium移动自动化测试-----(一)Appium介绍
- [傅里叶变换及其应用学习笔记] 七. 傅里叶正(反)变换复习
- C/C++中怎样获取日期和时间
- uniapp开发微信公众号(支付宝支付)
- 关于存储单元、寻址范围的问题
- 2015-华为招聘公开测试题目-单词迷宫
- 双硬盘笔记本电脑安装WIN10和Ubuntu双系统(二)
- Google镜像站点 资源收集
- Ubuntu 16.04 和18.04 命令行配置802.1x无线网络连接方法(针对北邮校园网BUPT-mobile)
- layui查档页面_首页--layui后台管理模板 2.0
- 几个opencv自带测试视频
热门文章
- 骑车与走路(YZOJ-1041)
- canvas绘图(下)
- 调侃:办公室“四大毒人”素描
- 《新概念英语3》大概相当于什么水平
- 8月9日逆水寒服务器维护时间,【图片】《逆水寒》2019年8月8日更新公告【逆水寒ol吧】_百度贴吧...
- audio标签控制音量_HTML5中audio标签的使用
- 景观设计主题命名_景观设计名字主题_园林景观好听的名字
- 香奈儿等奢侈品厂商要求欧盟修改网络销售法
- 一起了解Windows——把WPS设为.doc文件的默认打开应用
- python 人体建模_Google人体图像分割模型Bodypix再次更新,720p/30fps流畅运行