使用二叉树匹配IPV4黑名单

原理:将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"));

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值