原理:将IPV4转为32个bit来表示,生成一颗二叉树。
ip: 1.1.1.1 ---> 00000001000000010000000100000001
ip: 2.1.1.1 ---> 00000010000000010000000100000001
ip: 3.1.1.1 ---> 00000011000000010000000100000001
优点:因为二叉树的高度固定,所以每次匹配的次数也固定,每次匹配的复杂度是O(1),不会因为ip个数的增加匹配的时间也增加,是一种空间换时间的做法。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author linkaizhi
* @date 2022/3/23
*/
public class IPV4MatchUtils {
private class Node {
private Node left, right; // 0: left, 1: right
}
private Node root;
public IPV4MatchUtils() {
root = new Node();
}
/**
* 校验和解析ip
* @param ip
* @return
*/
private long checkAndConversionIP(String ip) {
String[] paragraph = ip.split("\\.");
if (4 != paragraph.length) {
throw new RuntimeException("ip格式错误");
}
List<Integer> numbers = Stream.of(paragraph)
.map(Integer::parseInt)
.filter(num -> num >= 0 && num <= 255)
.collect(Collectors.toList());
if (4 != numbers.size()) {
throw new RuntimeException("ip格式错误");
}
// 组装这32位
long ipNum = 0;
ipNum |= numbers.get(0) << 24;
ipNum |= numbers.get(1) << 16;
ipNum |= numbers.get(2) << 8;
ipNum |= numbers.get(3);
printlnIp(ip, ipNum);
return ipNum;
}
/**
* 打印ip
* @param ip
* @param ipNum
*/
private void printlnIp(String ip, long ipNum) {
long reversal = reversal(ipNum);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 32; ++i) {
long bit = reversal & 1;
stringBuilder.append((char)('0' + bit));
reversal >>= 1;
}
System.out.println("ip: " + ip + " ---> " + stringBuilder.toString());
}
/**
* 按位反转
* @param ip
* @return
*/
private long reversal(long ip) {
long resultIp = 0;
for (int i = 0; i < 32; ++i) {
long bit = ip & 1;
resultIp |= bit << (31 - i);
ip >>= 1;
}
return resultIp;
}
/**
* 添加一个ip
* @param ip
*/
private void add(long ip) {
Node index = root;
for (int i = 0; i < 32; ++i) {// 遍历这32位,把它放进二叉树里
long bit = ip & 1;// 取第一位
if (1 == bit) {
index.right = index.right == null ? new Node() : index.right;
index = index.right;
} else {
index.left = index.left == null ? new Node() : index.left;
index = index.left;
}
ip >>= 1;// 右移
}
}
/**
* 匹配
* @param ip
* @return
*/
private boolean match(long ip) {
Node index = root;
for (int i = 0; i < 32; ++i) {
long bit = ip & 1;
if (1 == bit && null != index.right) {
index = index.right;
} else if (0 == bit && null != index.left) {
index = index.left;
} else {
return false;
}
ip >>= 1;
}
return true;
}
/**
* 递归删除
* @param node
* @param ip
*/
private void removeDFS(Node node, long ip) {
if (null == node) {
return ;
}
long bit = ip & 1;
removeDFS(bit == 1 ? node.right : node.left, ip >> 1);
if (1 == bit) {
Node childNode = node.right == null ? new Node() : node.right;
if (null == childNode.left && null == childNode.right) {
node.right = null;
}
} else {
Node childNode = node.left == null ? new Node() : node.left;
if (null == childNode.left && null == childNode.right) {
node.left = null;
}
}
}
//-----------------------------------------------------------------------------------------------
/**
* 校验ip并且添加一个ip
* @param ip
*/
public boolean add(String ip) {
long ipNum = checkAndConversionIP(ip);
if (match(ipNum)) {// 已存在
return false;
}
add(ipNum);
return true;
}
/**
* 批量添加 左闭右闭
* @param ipLeft
* @param ipRight
*/
public void addRange(String ipLeft, String ipRight) {
long ipNumLeft = checkAndConversionIP(ipLeft);
long ipNumRight = checkAndConversionIP(ipRight);
if (ipNumLeft > ipNumRight) {
throw new RuntimeException("范围左边大于右边");
}
for (long i = ipNumLeft; i <= ipNumRight; ++i) {
add(i);
}
}
/**
* 删除一个ip
* @param ip
* @return
*/
public boolean remove(String ip) {
long ipNum = checkAndConversionIP(ip);
if (!match(ipNum)) {// 不存在
return false;
}
removeDFS(root, ipNum);
return true;
}
/**
* 删除一批ip
* @param ipLeft
* @param ipRight
*/
public void removeRange(String ipLeft, String ipRight) {
long ipNumLeft = checkAndConversionIP(ipLeft);
long ipNumRight = checkAndConversionIP(ipRight);
if (ipNumLeft > ipNumRight) {
throw new RuntimeException("范围左边大于右边");
}
for (long i = ipNumLeft; i <= ipNumRight; ++i) {
removeDFS(root ,i);
}
}
/**
* 匹配
* @param ip
* @return
*/
public boolean match(String ip) {
long ipNum = checkAndConversionIP(ip);
return match(ipNum);
}
public static void main(String[] args) {
IPV4MatchUtils ipMatchUtils = new IPV4MatchUtils();
// ipMatchUtils.add("1.1.1.1");
// System.out.println(ipMatchUtils.match("1.1.1.1"));//true
// System.out.println(ipMatchUtils.match("1.1.1.2"));//false
// System.out.println(ipMatchUtils.match("1.1.1.0"));//false
//
// ipMatchUtils.addRange("2.2.0.0", "2.2.9.255");
// System.out.println(ipMatchUtils.match("2.2.0.0"));//true
// System.out.println(ipMatchUtils.match("2.1.0.0"));//false
// System.out.println(ipMatchUtils.match("2.2.0.1"));//true
// System.out.println(ipMatchUtils.match("2.2.1.1"));//true
// System.out.println(ipMatchUtils.match("2.2.9.0"));//true
// System.out.println(ipMatchUtils.match("2.2.9.255"));//true
// System.out.println(ipMatchUtils.match("2.2.10.0"));//false
//
// System.out.println("-----------------------------------------------------");
// System.out.println(ipMatchUtils.add("123.123.123.123"));
// System.out.println(ipMatchUtils.add("123.123.123.123"));
// System.out.println(ipMatchUtils.add("123.123.123.123"));
// System.out.println(ipMatchUtils.add("123.123.123.123"));
//
// System.out.println("--------------------------------------------------------------------");
// System.out.println(ipMatchUtils.remove("123.123.123.123"));
// System.out.println(ipMatchUtils.remove("123.123.123.123"));
// System.out.println(ipMatchUtils.match("123.123.123.123"));
// System.out.println(ipMatchUtils.add("123.123.123.123"));
// System.out.println(ipMatchUtils.add("123.123.123.123"));
// System.out.println(ipMatchUtils.match("123.123.123.123"));
// System.out.println(ipMatchUtils.remove("123.123.123.123"));
//
// System.out.println("-----------------------------------------------------------------------------");
// ipMatchUtils.removeRange("2.2.0.0", "2.2.9.255");
// System.out.println(ipMatchUtils.remove("2.2.2.1"));
// System.out.println(ipMatchUtils.remove("2.2.2.2"));
System.out.println(ipMatchUtils.add("1.1.1.1"));
System.out.println(ipMatchUtils.add("2.1.1.1"));
System.out.println(ipMatchUtils.add("3.1.1.1"));
// System.out.println(ipMatchUtils.remove("1.1.1.1"));
// System.out.println(ipMatchUtils.remove("2.1.1.1"));
// System.out.println(ipMatchUtils.remove("3.1.1.1"));
//
// System.out.println(ipMatchUtils.match("1.1.1.1"));
}
}