IP地址归属地在线查询平台

一.项目介绍

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地址的不同形式的使用

二 .相关问题

强化项目思维

不管好方法还是差方法,先尝试把功能实现

再考虑对代码进行优化,最优为止

三 .主要思路

1 解析提供的地址库字符串,为结构化数据格式

2 基于结构化数据构建成某个数据结构,加速给定IP地址的查找速度

3 封装成响应的工具类API,开放其响应的方法,即给定IP地址可以再ms内计算得到其位置信息

4 工具类只有一个入参一个出参

入参是IP

出参是地址

四 .研发项目分类

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 项目总结,项目复盘

五 .代码研发

  1.  无脑读取文件
在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 关闭流
 */
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();
		}
	}
}

 

2.文本文件读取工具类

工具类封装 :

1 先写测试类,确认输入与输出技术问题

2 抽象出了输入与输出,形成方法的入参和出参

3 工具代码实现,测试

2.1工具类

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;
	}
}

2.1. 测试工具类

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);
		}
	}
}

 3. 数据结构化

1 可以找无结构化数据的组织规则,进行抽象,转换为结构化

2 根据数据组织进行数据抽象

3 解析数据,保存到对应的对象中

Model,pojo,dto

4. 抽象pojo类

4.1 抽象封装

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();
	}

}

4.2 数据拆分,转换为对象

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);
		}
	}
}

5. 封装DataProcessManager类

5.1 把数据封装到对象中并保存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;
	}
}

5.2 测试

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);
		}
	}
}

6. 结构化集合转换为数组

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);
		}

	}
}

7. 对象数组排序

7.1 实现排序

1. 自定义实现

冒泡排序

2. API

collections,Arrays

7.2 排序注意事项

被排序的元素必须具有可比性

实现Comparable接口

有比较器类Comparator

7.3 排序案例

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;
	}

}

7.4 总结

要么实现排序的类 实现Comparable接口

7.5业务问题

由于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();
	}
}

7.6 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;
		}
	}

}

7.7 测试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]);
	}
}

7.8 封装排序

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;
	}
}

7.9 测试

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]);
	}
}

8. 二分法

二分法查询,必须建立在已排序的基础上

8.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("没找着");
	 }

8,2 引用/复杂类型

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("没找着");
	
	 }

8.3 业务类实现

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("没找着");

	}

9. ip地址对象的范围形式数据的二分法实现

9.1 业务类功能实现

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;
	}
}

9.2 测试

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]);
	}
}

10.工具类封装

到目前为止,已经把二分法搞定了,已经可以实现功能了,测试代码就相当于客户端

但是目前客户端知道的信息还是比较多,客户只关心 入参和出参是什么就可以

至于我们用什么编码,用什么存储,客户根本不关心

此时我们需要提供一个对外访问的方法,该方法入参就是IP 出参就是归属地

10.1 编码

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;
	}
}

10.2 测试

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);
	}
}

10.3 优化

功能实现了,像以下情况,同一个生命周期中,如果操作两次及两次以上

那么 会导致 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;
	}
}

10.4 测试

 

 11. 入口类

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));
		}

	}
}

12. bug优化

用户输入 需要严格校验,比如IP地址不正确,还有必要去查询吗?

12.1 技术问题

正则表达式
Pattern和Matcher
Pattern是正则表达式引擎
Matcher是匹配器
Matches : 全词匹配
Find : 任意位置
lookingAt : 从前往后匹配
package com;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TestRegex_01 {
	public static void main(String[] args) {
		String regex = "\\d{11}";
		String tel = "131131131121";
		// 创建引擎
		Pattern pattern = Pattern.compile(regex);
		// 创建匹配器
		Matcher matcher = pattern.matcher(tel);
		// System.out.println(matcher.matches());
		// System.out.println(matcher.find());
		System.out.println(matcher.lookingAt());
		tel = "我的电话是13113113111";
		regex = "我的电话是(\\d{11})";
		pattern = Pattern.compile(regex);
		matcher = pattern.matcher(tel);
		matcher.find();
		System.out.println(matcher.group(1));
	}
}

12.2 封装工具类

package com.tledu.zrz.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则表达式工具类
 * 
 * @author 天亮教育-帅气多汁你泽哥
 * @Date 2021年11月12日 上午9:37:50
 */
public class RegexUtil {
	/**
	 * 格式验证
	 * 
	 * @param regex
	 * @param input
	 * @return
	 */
	public static boolean isValid(String regex, String input) {
		Pattern pattern = Pattern.compile(regex);
		// 创建匹配器
		Matcher matcher = pattern.matcher(input);
		return matcher.matches();
	}

	/**
	 * 数据提取
	 * 
	 * @param regex
	 * @param input
	 * @param groupIndex
	 * @return
	 */
	public static String getMatchContent(String regex, String input,
			int groupIndex) {
		Pattern pattern = Pattern.compile(regex);
		// 创建匹配器
		Matcher matcher = pattern.matcher(input);
		if (matcher.find()) {
			return matcher.group(groupIndex);
		}
		return null;
	}

	public static String getMatchContent(String regex, String input) {
		return getMatchContent(regex, input, 0);
	}
}

12.3 测试工具类

package com;

import com.tledu.zrz.util.RegexUtil;

public class TestRegex_02 {
	public static void main(String[] args) {
		String regex = "\\d{11}";
		String tel = "13113113111";
		System.out.println(RegexUtil.isValid(regex, tel));
		tel = "我的电话是13113113111";
		regex = "我的电话是(\\d{11})";
		System.out.println(RegexUtil.getMatchContent(regex, tel,1));
	}
}

12.4 业务问题

package com.tledu.zrz.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则表达式工具类
 * 
 * @author 天亮教育-帅气多汁你泽哥
 * @Date 2021年11月12日 上午9:37:50
 */
public class RegexUtil {
	private static String regexIP = "((25[0-5]|2[0-4]\\d|[1]{1}\\d{1}\\d{1}|[1-9]{1}\\d{1}|\\d{1})($|(?!\\.$)\\.)){4}";

	/**
	 * 校验IP
	 * @param ip
	 * @return
	 */
	public static boolean isValidIP(String ip) {
		return isValid(regexIP, ip);
	}

	/**
	 * 格式验证
	 * 
	 * @param regex
	 * @param input
	 * @return
	 */
	public static boolean isValid(String regex, String input) {
		Pattern pattern = Pattern.compile(regex);
		// 创建匹配器
		Matcher matcher = pattern.matcher(input);
		return matcher.matches();
	}

	/**
	 * 数据提取
	 * 
	 * @param regex
	 * @param input
	 * @param groupIndex
	 * @return
	 */
	public static String getMatchContent(String regex, String input,
			int groupIndex) {
		Pattern pattern = Pattern.compile(regex);
		// 创建匹配器
		Matcher matcher = pattern.matcher(input);
		if (matcher.find()) {
			return matcher.group(groupIndex);
		}
		return null;
	}

	public static String getMatchContent(String regex, String input) {
		return getMatchContent(regex, input, 0);
	}
}

12.5 测试

package com;

import com.tledu.zrz.util.RegexUtil;

public class TestRegex_03 {
	public static void main(String[] args) {
		String ip = "1.1.322.5";
		System.out.println(RegexUtil.isValidIP(ip));
	}
}

12.6 应用

校验应该写在对外提供的接口处

getLocation

 13. 性能测试

13.1 相关技术

13.1.1硬件

内存,CPU.磁盘,网络等 都可以实现性能提高

13.1.2 软件

13.1.2.1 直接调优

哪里慢,就调哪里,不需要花里胡哨的

主要指算法问题,内核层面,难度较大,大部分都是非直接调优

13.1.2.2 迂回调优

通过设计,策略,可以通过不用面对底层优化的难题

13.1.2.3 迂回调优方向

缓存策略

通过添加时间断点,测试得出,结构化耗时较多

一开始,我们一次运行中,先后校验两个IP,需要结构化两次

后来我们使用静态语句块解决了这个问题

做到一次生命周期中,只会结构化一次

现在面临的问题是,需要让多次生命周期,使用同一个结构化之后的对象,就可以解决当前的问题

可以使用序列化和反序列化解决

 13.1.3 序列化和反序列化

序列化 : 将内存中对象保存到硬盘中

反序列化 : 把硬盘中对象载入到内存

被序列化的对象必须实现serlizable接口

13.2 首次调优

13.2.1 技术问题

package com;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TestSerinalizable_01 {
	public static void main(String[] args) throws Exception{
		User user =new User(11);
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/x"));
		oos.writeObject(user);
		oos.close();
		
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/x"));
		Object o = ois.readObject();
		System.out.println(o);
	}
}
class User implements Serializable{
	private int age;

	public User(int age) {
		super();
		this.age = age;
	}

	@Override
	public String toString() {
		return "User [age=" + age + "]";
	}
	
}

13.2.2 封装工具类

package com.tledu.zrz.util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerDeUtil {
	/**
	 * 写入
	 * 
	 * @param obj
	 * @param objFilePath
	 * @throws IOException
	 */
	public static void saveObject(Object obj, String objFilePath)
			throws IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
				objFilePath));
		oos.writeObject(obj);
		oos.close();
	}

	/**
	 * 读取
	 * 
	 * @param objFilePath
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	public static Object getObj(String objFilePath)
			throws ClassNotFoundException, IOException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
				objFilePath));
		Object o = ois.readObject();
		return o;
	}
}

13.2.3 测试

package com;

import com.tledu.zrz.util.SerDeUtil;

public class TestSerinalizable_02 {
	public static void main(String[] args) throws Exception{
		User user =new User(11);
		SerDeUtil.saveObject(user, "D:/a");
		
		System.out.println(SerDeUtil.getObj("D:/a"));
	}
}

13.2.4 初始化优化

只有第一次才序列化,其余的都反序列化即可

判断该文件是否存在,如果存在,就反序列化获取,如果不存在,就序列化写出

注意

public class IPAndLocationPojo implements Serializable,
		Comparable<IPAndLocationPojo> {


package com.tledu.zrz.manager;

import com.tledu.zrz.pojo.IPAndLocationPojo;
import com.tledu.zrz.util.FileOperatorUtil;
import com.tledu.zrz.util.IPUtil;
import com.tledu.zrz.util.RegexUtil;
import com.tledu.zrz.util.SerDeUtil;

import java.io.File;
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";
		// 文件地址
		String serde_obj_filepath = "ipLibObj.data";
		// 判断序列化文件是否存在
		boolean isInit = new File(serde_obj_filepath).exists();
		// 存在就反序列化获取
		if (isInit) {
			try {
				Object object = SerDeUtil.getObj(serde_obj_filepath);
				ipLocationPojoArr = (IPAndLocationPojo[]) object;
			} catch (ClassNotFoundException | IOException e) {
				e.printStackTrace();
			}
		}else{
		// 不存在就序列化写出
			// 获取数据
			List<IPAndLocationPojo> ipAndLocationPojos = null;
			try {
				ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath,
						encoding);
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 转换为数组
			ipLocationPojoArr = DataProcessManager
					.convertListToArrayAndSort(ipAndLocationPojos);
			try {
				SerDeUtil.saveObject(ipLocationPojoArr, serde_obj_filepath);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	
	}

	/**
	 * 对外提供的接口,入参IP出参地址
	 * 
	 * @param ip
	 * @return
	 */
	public static String getLocation(String ip) {
		if (RegexUtil.isValidIP(ip)) {
			int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip);
			return index == -1 ? "未找到" : ipLocationPojoArr[index].getLocation();
		}else{
			return "IP地址格式不正确,请重新输入";
		}

	}

	/**
	 * 业务类对应的二分法
	 * 
	 * @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;
	}
}

13.3 IO调优

加入序列化和反序列化之后,导致效率更低,并且第二个运行要比第一次还慢,说明反序列化有问题

缓冲流 : 缓冲流是IO流的缓冲区,用来提高IO的效率

首次 7000

非首次 20000

文件大小为 38.3M

13.3.1 引入缓冲流

package com.tledu.zrz.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerDeUtil {
	/**
	 * 对象写出,引入缓冲流
	 * 
	 * @param obj
	 * @param objFilePath
	 * @param cacheByyeArrayLength
	 * @throws IOException
	 */
	public static void saveObject(Object obj, String objFilePath,
			int cacheByyeArrayLength) throws IOException {
		FileOutputStream fos = new FileOutputStream(objFilePath);
		// 字节数组缓冲流
		ByteArrayOutputStream baos = new ByteArrayOutputStream(
				cacheByyeArrayLength);
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		oos.writeObject(obj);
		byte[] bytes = baos.toByteArray();
		fos.write(bytes);
		oos.close();
		fos.close();
	}

	/**
	 * 获取,引入缓冲流
	 * 
	 * @param objFilePath
	 * @param cacheByyeArrayLength
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	public static Object getObj(String objFilePath, int cacheByyeArrayLength)
			throws ClassNotFoundException, IOException {
		FileInputStream fis = new FileInputStream(objFilePath);
		byte[] bytes = new byte[cacheByyeArrayLength];
		fis.read(bytes);
		fis.close();
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
		ObjectInputStream ois = new ObjectInputStream(bais);
		Object o = ois.readObject();
		ois.close();
		return o;
	}

	/**
	 * 写入
	 * 
	 * @param obj
	 * @param objFilePath
	 * @throws IOException
	 */
	public static void saveObject(Object obj, String objFilePath)
			throws IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
				objFilePath));
		oos.writeObject(obj);
		oos.close();
	}

	/**
	 * 读取
	 * 
	 * @param objFilePath
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	public static Object getObj(String objFilePath)
			throws ClassNotFoundException, IOException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
				objFilePath));
		Object o = ois.readObject();
		ois.close();
		return o;
	}
}

13.3.2 调用处更改

 

 

更改之后 首次 3000  非首次 1000

13.4 代码标准化

 

代码中,出现了好多这些变量,当我们需要更改某一个的时候,不好找

集中管理

13.4.1 StaticValue

package com.tledu.zrz.util;

public class StaticValue {
	// 地址库文件
	public static String ipLibrartPath = "ip_location_relation.txt";
	// 字符编码
	public static  String encoding = "utf-8";
	// 文件地址
	public static  String serde_obj_filepath = "ipLibObj.data";
	// 根据文件大小,计算长度
	public static  int cacheByteArrayLength = 40*1024*1024;
}

13.4.2 应用

// 判断序列化文件是否存在
		File file = new File(StaticValue.serde_obj_filepath);
		boolean isInit = file.exists();
		// 存在就反序列化获取
		if (isInit) {
			try {
				Object object = SerDeUtil.getObj(
						StaticValue.serde_obj_filepath,
						StaticValue.cacheByteArrayLength);
				ipLocationPojoArr = (IPAndLocationPojo[]) object;
			} catch (ClassNotFoundException | IOException e) {
				e.printStackTrace();
			}
		} else {
			// 不存在就序列化写出
			// 获取数据
			List<IPAndLocationPojo> ipAndLocationPojos = null;
			try {
				ipAndLocationPojos = DataProcessManager.getPojoList(
						StaticValue.ipLibrartPath, StaticValue.encoding);
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 转换为数组
			ipLocationPojoArr = DataProcessManager
					.convertListToArrayAndSort(ipAndLocationPojos);
			try {
				SerDeUtil.saveObject(ipLocationPojoArr,
						StaticValue.serde_obj_filepath,
						StaticValue.cacheByteArrayLength);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

14. 调优进阶

14.1.1 IO优化

直接优化 : 提高IO效率

间接优化 : 数据大小也会应该效率问题

文件大小变动

首次2500 非首次 520-600

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

让火车在天上飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值