ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

纯java从0到1实现一个布隆过滤器

2021-11-14 22:02:13  阅读:118  来源: 互联网

标签:return int 布隆 static key 过滤器 java public hash


纯java从0到1实现一个布隆过滤器

gitee地址

本文简介布隆过滤器原理

主要介绍如何使用java实现一个bloom filter

一、位图

我们知道布隆过滤器实际上就是一个超级大的位图;

上面维护了所有黑名单(或者白名单)数据。

一个key过来或根据hash运算获取到所有对应位图上的点位,判断这些点位是否全为1.

  • 如果全为1,那么判定此key已经添加进了位图(存在一定误判率)
  • 若果有一个不为1,那么此key一定没有添加进位图(不存在误判)

二、几个重要参数

  • 样本量(个):n
  • 失误率(小数):p
  • 位图空间(bit即位):m
  • 使用hash函数个数:k

在生成初始化一个布隆过滤器时,用户需要选择的是样本量n,和失误率p。

然后根据公式计算得出当前的位图空间,使用的hash函数个数k,

最终计算得出真实的失误率p

三、公式

  1. 所需的位图空间m

    m = -[n*ln§]/[ln(2)^2]

  2. 所需的hash函数个数k

    k = ln(2)*(m/n)

  3. 真实的失误率P

    P = {1-e[-(n*K)/M]}K

代码

一、布隆过滤器主类

此类中维护了一个long的数组,作为过滤器的位图,也是实现布隆过滤器的关键。

private static final long[] MEMORY;

java中定义数组大小时使用的是int整数。

也就是数组大小最多是2^31-1个

去最大的2的指数就是2^30个

一个long保存2^6个信息

也就是MEMORY最多保存2^36个位信息即1TB内存空间。

由于我所使用的hash函数运算的结果都是32位的int型数据。

对应的是32根地址线,也就是2^32bit=512MB;

所以布隆过滤器位图的最大内存空间只有3=512MB;

分析一个32位的hashcode结果如何插入到对应的MEMORY数组中。

我们知道,一个long型变量占用8字节64位。

所以MEMORY数组中每一个元素可以存放64个元素(也就是2的6次方个元素)

那么2x位大小的位图空间,就需要开辟2(x-6)个数组。

这是需要的总的地址位数就是x(前x-6表示数组下标,后6位表示对应数组的哪一位)。

假设现在有一个数32位的int数i要插入到位图对应的空间中

i的二进制表示为

i= 0100 1001 0101 1110 1001 0111 1011 1100

假设x = 30,也就是需要24位表示数组下标,6位表示对应元素

也就是i的中间第3到底27位需要作为数组的下标信息。

​ d= 0011 1111 1111 1111 1111 1111 1100 0000 使用d&i运算就得到了索引下标index

​ i= 0100 1001 0101 1110 1001 0111 1011 1100

index = 0000 1001 0101 1110 1001 0111 1000 0000

这个24也就对应了下面代码中的SAVE_SIZE

index的运算代码如下:

int index = (-1 >>> 32 - SAVE_SIZE << 6 & i) >>> 6

这里理清后,对应的布隆过滤器的插入和删除操作也就不在是难题了。

插入操作:

  1. 首先计算出对应的数组下标
  2. 然后获取i的后六位二进制信息code得到对应的数组位置。
  3. 最后将code插入到位图中
public static void insert(int i) {
        //索引下标   (生成索引下边的具体步骤请移步MathTest::getIndex)
        int index = (-1 >>> 32 - SAVE_SIZE << 6 & i) >>> 6;
        //获取i对应数组元素中的位下标(这里的计算方式和HashMap获取元素在数组的位置的方式一致)
        int code = i & 63;
        //将数组元素中对应位置为1
        MEMORY[index] = MEMORY[index] | (1 << code);
    }

检查操作

  1. 同样先获取对应的数组下标
  2. 然后获取后六位信息
  3. 然后获取对应数组索引下对应的位信息i1
  4. 判断这个位信息是否是1
    • 如果是1代表这个i已经插入到了位图中(存在误差,这也就是布隆过滤器误判存在的根源)
    • 如果是0代表这个i一定没有插插入过位图
 public static boolean check(int i) {
        int index = (-1 >>> 32 - SAVE_SIZE << 6 & i) >>> 6;//获取数组的位置下标
        int code = i & 63;//截取i的后六位信息(也就是此i对应MEMERY元素的哪个位)
        long i1 = MEMORY[index] >>> code;//将i对应的位右移code位将i对应的位移动到最低位
        long i2 = i1 & 1;//将i1与1进行与操作获取此i是否在MEMORY位图中。
        return i2 == 1;
    }

其他操作

布隆过滤器的主要业务逻辑都在插入和检查中

下面简略介绍一些额外的操作

  1. 读取配置文件

    过滤器主类获取n(样本量)和p(期望误判率)的方式是通过配置文件的方式获取的。

    读取到所需的配置项后返回一个Map集合(这个集合在静态代码块中使用)

    /**
         * read config properties file ,and return params map
         * @return
         */
        public static Map<String,Object> initProperties(){
            HashMap<String, Object> map = new HashMap<>();
            Properties properties = new Properties();
            // 使用ClassLoader加载properties配置文件生成对应的输入流
            ClassLoader classLoader = BloomFilter.class.getClassLoader();
            logger.info(classLoader);
            InputStream in = classLoader.getResourceAsStream("config/config.properties");
            // 使用properties对象加载输入流
            String n = null;
            String memoryStr = n;
            MemorySize mb = null;
            String p = null;
            try {
                properties.load(in);
                //memoryStr = properties.getProperty("memory");
                n = properties.getProperty("number");
                p = properties.getProperty("missProbability");
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (n == null || p == null) {
                throw noPropertiesException;
            }
            map.put("NUMBER",Long.parseLong(n));
            map.put("MISSPROBABILITY",Double.parseDouble(p));
            return map;
        }
    
  2. 静态代码块

    调用读取配置文件的initProperties方法,给全部的静态常量赋值。

    包括 NUMBER样本个数
    MISSPROBABILITY期望的失误率
    REALMISSPROBABILITY;真实失误率
    REALSIZE实际开辟内存空间(返回的是一个可视化的字符串)
    K_USEHASHCODENUMBER使用的hashcode函数个数
    REALMEMORYBIT实际开辟内存空间(位)

    /**
         * 静态代码块
         * 所有的计算性质都放置在静态代码块中实现,当类初始化时一次性计算生成,避免重复计算浪费性能
         */
        static {
            Map<String, Object> npMap = initProperties();
            NUMBER =(Long) npMap.get("NUMBER");
            MISSPROBABILITY =(Double) npMap.get("MISSPROBABILITY");
            long m = getNeedMemorySize(NUMBER, MISSPROBABILITY);
            int k = getHashCodeMethodNumber(NUMBER, m);
            REALMEMORYBIT = getRealMemorySize(NUMBER, MISSPROBABILITY);
            REALMISSPROBABILITY = getRealMissProbability(NUMBER, REALMEMORYBIT, k);
            REALSIZE = MemoryForAddressNumber.inBitOutSize(REALMEMORYBIT);
            int save_size = 0;
            long flag = REALMEMORYBIT;
            while (((flag=flag>>1)&1)==0){
                save_size++;
            }
            SAVE_SIZE = save_size -5;
            int size = 1 << SAVE_SIZE;
            MEMORY = new long[size];
            K_USEHASHCODENUMBER = getHashCodeMethodNumber(NUMBER, REALMEMORYBIT);
    
            logger.info("MOVE:"+ SAVE_SIZE);
            logger.info("realMemorySize = " + REALMEMORYBIT);
            logger.info("实际开辟的空间:"+REALSIZE);
            logger.info("K:"+k);
            logger.info("m"+m);
    
        }
    
  3. 读取配置文件后就可以计算的出其余各项的值,一些计算方法都放在了主类的最后

主类的完整代码

package top.boking.bloom;

import org.apache.log4j.Logger;

import top.boking.exception.HashCodeMethodNumberOutOfException;
import top.boking.exception.HashCodeOutofMemoryException;
import top.boking.exception.NoPropertiesException;
import top.boking.utils.HashCodeLib;
import top.boking.utils.MemoryUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 采用long型数组存储元素
 */
public class BloomLong {
    private BloomLong() {

    }

    private static Logger logger = Logger.getLogger(BloomFilter.class);//log4j日志对象
    private static final long[] MEMORY;//位图数组,布隆过滤器数据集合,黑名单一旦加入不可删除(或者说暂不支持删除亦或者说极难删除)。
    private static final int HASHCODEMETHODNUMBER;//HashCodeLib提供的hash函数个数
    private static final int SAVE_SIZE;//生成对应的内存空间的数组大小的保留位数(不太好理解,或许有更好的方式实现)。
    //非不可变属性一定得是私有的。

    private static long nowDataNumber = 0;//当前的实际样本量
    //公有参数一定得是final不可变的,这里的公有参数可以全部设计成私有,然后提供get方法。
    public static final long NUMBER;//样本个数
    public static final double MISSPROBABILITY;//需求的失误率
    public static final double REALMISSPROBABILITY;//真实失误率
    public static final String REALSIZE;//实际开辟内存空间(可视化的字符串类型)
    public static final int K_USEHASHCODENUMBER;//使用hash函数个数

    public static final long REALMEMORYBIT;//实际开辟内存空间(位)
    //定义异常
    private static HashCodeOutofMemoryException hashCodeOutofMemoryException = new HashCodeOutofMemoryException();
    private static HashCodeMethodNumberOutOfException numberOutOfException = new HashCodeMethodNumberOutOfException();
    private static NumberFormatException numberformatexception = new NumberFormatException();
    private static NoPropertiesException noPropertiesException = new NoPropertiesException();


    /**
     * 静态代码块,从配置文件中获取
     * 所有的计算性质都放置在静态代码块中实现,当类初始化时一次性计算生成,避免重复计算浪费性能
     */
    static {
        HASHCODEMETHODNUMBER = HashCodeLib.getHashcodemethodnumber();
        Map<String, Object> npMap = initProperties();
        NUMBER = (Long) npMap.get("NUMBER");
        MISSPROBABILITY = (Double) npMap.get("MISSPROBABILITY");
        long m = getNeedMemorySize(NUMBER, MISSPROBABILITY);
        int k = getHashCodeMethodNumber(NUMBER, m);
        REALMEMORYBIT = getRealMemorySize(NUMBER, MISSPROBABILITY);
        REALMISSPROBABILITY = getRealMissProbability(NUMBER, REALMEMORYBIT, k);
        REALSIZE = MemoryUtils.inBitOutSize(REALMEMORYBIT);
        int save_size = 0;
        long flag = REALMEMORYBIT;
        while (((flag = flag >> 1) & 1) == 0) {
            save_size++;
        }
        SAVE_SIZE = save_size - 5;
        int size = 1 << SAVE_SIZE;
        MEMORY = new long[size];
        K_USEHASHCODENUMBER = getHashCodeMethodNumber(NUMBER, REALMEMORYBIT);
        if (K_USEHASHCODENUMBER > HASHCODEMETHODNUMBER) {
            throw hashCodeOutofMemoryException;
        }
        logger.info("MOVE:" + SAVE_SIZE);
        logger.info("realMemorySize = " + REALMEMORYBIT);
        logger.info("实际开辟的空间:" + REALSIZE);
        logger.info("实际使用的hashcode个数" + k);
        logger.info("m" + m);

    }

    public static void init() {

    }

    /**
     * read config properties file ,and return params map
     *
     * @return
     */
    public static Map<String, Object> initProperties() {
        HashMap<String, Object> map = new HashMap<>();
        Properties properties = new Properties();
        // 使用ClassLoader加载properties配置文件生成对应的输入流
        ClassLoader classLoader = BloomFilter.class.getClassLoader();
        logger.info(classLoader);
        InputStream in = classLoader.getResourceAsStream("config/config.properties");
        // 使用properties对象加载输入流
        String n = null;
        String memoryStr = n;
        MemorySize mb = null;
        String p = null;
        try {
            properties.load(in);
            //memoryStr = properties.getProperty("memory");
            n = properties.getProperty("number");
            p = properties.getProperty("missProbability");
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (n == null || p == null) {
            throw noPropertiesException;
        }
        map.put("NUMBER", Long.parseLong(n));
        map.put("MISSPROBABILITY", Double.parseDouble(p));
        return map;
    }

    /**
     * 将对应输入的i插入到位图memory中
     * 应该是private属性,测试用时,可以放开成public进行指定i的插入测试。
     *
     * @param i 输入的hashcode值
     */
    public static void insert(int i) {
        //索引下标   (生成索引下边的具体步骤请移步MathTest::getIndex)

        int index = (-1 >>> 32 - SAVE_SIZE << 6 & i) >>> 6;
        //获取i对应数组元素中的位下标(这里的计算方式和HashMap获取元素在数组的位置的方式一致)
        int code = i & 63;
        //将数组元素中对应位置为1
        MEMORY[index] = MEMORY[index] | (1L << code);
    }

    /**
     * 对外的insert方法,
     * 调用NUMBER次对内的insert()方法,将此key对应的所有标志插入到memory中
     *
     * @param key
     * @return
     */
    public static boolean insertKey(String key) {
        if (nowDataNumber >= NUMBER) {
            logger.warn("警告:插入的样本已经超过设计值NUMBER,继续插入失误率可能会超出预设范围");
        }
        try {
            for (int i = 0; i <= K_USEHASHCODENUMBER; i++) {
                Integer integer = HashCodeLib.choiceHashMethod(key, i);
                logger.trace(i);
                insert(integer);
            }
        } catch (Exception e) {
            return false;
        }
        nowDataNumber++;//添加成功后将实际样本量加1。
        return true;
    }

    /**
     * 快速插入,无差错控制
     * @param key
     * @return
     */
    public static void insertFast(String key) {
            for (int i = 0; i <= K_USEHASHCODENUMBER; i++) {
                Integer integer = HashCodeLib.choiceHashMethod(key, i);
                logger.trace(i);
                insert(integer);
            }
        nowDataNumber++;//添加成功后将实际样本量加1。
    }


    /**
     * 同步代码块包裹的插入方法方法
     * 简单对insertKey进行了包裹
     * 应该还可以优化
     *
     * @param key
     * @return
     */
    public static boolean insertKeySecurity(String key) {
        synchronized (BloomFilter.class) {
            return insertKey(key);
        }
    }

    /**
     * 针对特定的输入i进行检测
     * 检测对应位图上是否置1,
     *
     * @param i
     * @return
     */
    public static boolean check(int i) {
        int index = (-1 >>> 32 - SAVE_SIZE << 6 & i) >>> 6;//获取数组的位置下标
        int code = i & 63;//截取i的后五位信息(也就是此i对应MEMERY元素的哪个位)
        long i1 = MEMORY[index] >>> code;//将i对应的位右移code位将i对应的位移动到最低位
        long i2 = i1 & 1;//将i1与1进行与操作获取此i是否在MEMORY位图中。
        return i2 == 1;

        //五步合并后,编译器化简的最终结果,这结果神仙应该也不知道这是在干啥吧。
//              return  1==((MEMORY[(-1 >>> 32 - MOVE << 6 & i ) >>> 6] >>> (i & 63))&1);
    }

    /**
     * 对外的check方法
     * 调用NUMBER次对内的check()方法,检查此key是否在名单里
     * 一旦某次check()返回位false则可确保
     *
     * @param key
     * @return
     */
    public static boolean checkKey(String key) {

        for (int i = 0; i < K_USEHASHCODENUMBER; i++) {
            Integer code = HashCodeLib.choiceHashMethod(key, i);
            if (!check(code))
                return false;
        }

        return true;
    }


    //以下是计算方法

    /**
     * 根据样本量和失误率计算所需空间的公式:
     * m = - [n*ln(p)]/[(ln2)^2]
     *
     * @param n 样本量(个)
     * @param p 失误率(小数)
     * @return 理论所需空间(bit)
     */
    public static long getNeedMemorySize(long n, double p) {
        double high = n * Math.log(p);
        double low = Math.log(2) * Math.log(2);
        long m = (long) -(high / low);//这里需要向上取整所以number要+1,但是进行
        return m + 1;
        //long number = m +1-1;
        /*long flag = Long.MIN_VALUE;
        while ((flag & m) == 0) {
            flag >>>= 1;
        }
        return flag<<1;*/

    }

    /**
     * 计算出实际分配的内存空间(位)
     *
     * @param n 样本量
     * @param p 所需失误率
     * @return 实际分配的内存空间(位)
     */
    public static long getRealMemorySize(long n, double p) {
        long m = getNeedMemorySize(n, p) - 1;

        long flag = Long.MIN_VALUE;
        while ((flag & m) == 0) {
            flag >>>= 1;
        }
        return flag << 1;

    }

    /**
     * 根据样本量和理论所需空间计算所需的hash函数个数k
     *
     * @param n 样本量
     * @param m 理论所需空间
     * @return
     */
    public static int getHashCodeMethodNumber(long n, long m) {
        int k = (int) (Math.log(2) * (m / n));
        return k + 1;
    }

    /**
     * 计算实际的失误率
     *
     * @param n 样本量
     * @param m 实际分配空间(位)
     * @param k 实际的hash函数个数
     * @return 真实失误率
     */
    public static double getRealMissProbability(long n, long m, int k) {
        float f = n * k;
        float e = f / m;
        return Math.pow((1 - Math.pow(Math.E, -e)), k);
    }

    /**
     * 返回当前实际的样本量。
     *
     * @return
     */
    public static long getNowDataNumber() {
        return nowDataNumber;
    }

    public static double getNowMissProbability() {

        double f = nowDataNumber * K_USEHASHCODENUMBER;
        double e = f / REALMEMORYBIT;
        return Math.pow((1 - Math.pow(Math.E, -e)), REALMEMORYBIT);
    }


}

二、工具类

hash函数工具类

此工具类为过滤器主类提供方法支持

主要工作逻辑是:

在静态代码块中,通过反射获取到本类中的hash函数个数

将它们添加到全局的methodList集合中

然后在过滤器调用choiceHashMethod方法时通过反射的方式执行对应的hashcode

(这样设计,方便hash函数的扩容,但是,可能会影响性能,可以将反射执行的方式更换为Switch语句直接执行方法,来提高性能,这样就失去了hash函数动态扩展的能力)

包括16个添加了@HashType()注解的hash函数

package top.boking.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * @author shxl
 * 哈希函数工具类库,集合了我自定义的各种hash函数
 */
public class HashCodeLib {
    private HashCodeLib() {
    }

    //hash当前类的函数个数
    private static final int HASHCODEMETHODNUMBER;

    private static final ArrayList<Method> methodList;//其实直接使用ArrayList就已经可以实现了。而且效率甚至更高

    /**
     * 通过反射获取当前类中的hash函数方法的个数,赋值给hashcodemethodnumber
     * (静态代码块编写,仅在类加载时执行一次,减少开销)
     */
    static {
        Method[] methods = HashCodeLib2.class.getDeclaredMethods();
        methodList = new ArrayList<>();
        int count = 0;
        if(methods != null){
            for(Method method : methods){
                HashType annotation = method.getAnnotation(HashType.class);
                if(annotation == null)
                    continue;
                methodList.add(method);
            }
        }
        methodList.forEach((k)->{
            System.out.println(k);
        });
        HASHCODEMETHODNUMBER = methodList.size();
    }

    public static int getHashcodemethodnumber(){
        return HASHCODEMETHODNUMBER;
    }
    public static Integer choiceHashMethod(String key, int kind) {
        Method method = methodList.get(kind);
        Class stringClass = String.class;
        try {
            int sxl =(int) method.invoke(stringClass,key);
            return sxl;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        /*switch (kind) {
            case 0:
                return hashCodeADD(key);
            case 1:
                return hashCodeRotating(key);
            case 2:
                return hashCodeFNV(key);
            case 3:
                return hashCodeRS(key);
            case 4:
                return hashCodeFNVandADD(key);
            case 5:
                return hashCodeFNVandRS(key);
            case 6:
                return hashCodeFNVandRS(key);
            case 7:
                return hashCodeFNVandRS(key);
            case 8:
                return hashCodeFNVandRS(key);
            case 9:
                return hashCodeFNVandRS(key);
            case 10:
                return hashCodeFNVandRS(key);
            case 11:
                return hashCodeFNVandRS(key);
            case 12:
                return hashCodeFNVandRS(key);
            case 13:
                return hashCodeFNVandRS(key);
            case 14:
                return hashCodeFNVandRS(key);
            case 15:
                return hashCodeFNVandRS(key);
            case 16:
                return hashCodeFNVandRS(key);

        }*/
        return null;
    }

    /**
     * FNV的hash算法
     *
     * @param key
     * @return 返回32位hash函数值
     */
    @HashType(HashLevel.HIGH)
    public static int hashCodeFNV(String key) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < key.length(); i++) {

            hash = (hash ^ key.charAt(i)) * p;
            hash += hash << 13;
            hash ^= hash >> 7;
            hash += hash << 3;
            hash ^= hash >> 17;
            hash += hash << 5;
        }
        return hash;
    }

    /**
     * 加法型hash
     *
     * @param key hash输入值
     * @return
     */
    @HashType(HashLevel.NORMAL)
    public static int hashCodeADD(String key) {

        int hash, i;
        for (hash = key.length(), i = 0; i < key.length(); i++)
            hash += key.charAt(i);
        return hash;

    }

    /**
     * 标准的旋转hash, 通过先移位,在进行各种位运算。 比如某个学校同一系的新生,学号前5位相同,最后2位不同,
     * 将最后2位旋转放置前两位,其余的右移。这样只要输入前两位,就可以快速查出学生的信息。
     * @param key hash key
     */
    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotating(String key) {
        int hash;
        int i;
        for (hash = key.length(), i = 0; i < key.length(); i++) {
            hash = (hash<<4)^(hash>>28)^key.charAt(i);
        }
        return hash ;  //除以 一个prime的目的只是为了保证结果的范围
    }

    /**
     * 乘以一个不断该不变的数
     * @param key hash key
     * @return hash value
     */
    @HashType(HashLevel.NORMAL)
    public static int hashCodeRS(String key) {
        int b = 37855;
        int a = 63689;
        int hash = 0;
        for (int i = 0; i < key.length(); i++) {
            hash = hash * a + key.charAt(i);
            a = a * b;
        }
        return hash ;
    }



    @HashType(HashLevel.NORMAL)
    private static int hashCodeFNVandADD(String key){

        return hashCodeFNV(key)&hashCodeADD(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeFNVorADD(String key){
        return hashCodeFNV(key) | hashCodeADD(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeFNVxorADD(String key){
        return hashCodeFNV(key) ^ hashCodeADD(key);
    }


    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotatingxorADD(String key){
        return hashCodeRotating(key) ^ hashCodeADD(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotatingorADD(String key){
        return hashCodeRotating(key) | hashCodeADD(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotatingandADD(String key){
        return hashCodeRotating(key)& hashCodeADD(key);
    }



    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotatingorRS(String key){
        return hashCodeRotating(key) | hashCodeRS(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotatingandRS(String key){
        return hashCodeRotating(key) &  hashCodeRS(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeRotatingxorRS(String key){
        return hashCodeRotating(key) ^  hashCodeRS(key);
    }

    @HashType(HashLevel.NORMAL)
    private static int hashCodeFNVorRS(String key){
        return hashCodeFNV(key) | hashCodeRS(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeFNVandRS(String key){
        return hashCodeFNV(key) &  hashCodeRS(key);
    }
    @HashType(HashLevel.NORMAL)
    private static int hashCodeFNVxorRS(String key){
        return hashCodeFNV(key) ^  hashCodeRS(key);
    }




}

依赖的注解类

在HashCodeLib的方法上添加@HashType注解,表示这是一个hash函数计算方法。

枚举类代表这些hash函数的优劣(没有进行特别的检测,性能都是随便标识的)

package top.boking.utils;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
//暂时将这个注解的作用域设置成runtime,如有必要再进行修改
@Retention(RetentionPolicy.RUNTIME)
/**
 * 本注解配合HsahLevel枚举类,补充HashCodeLib类库中的hashcodemethod的信息
 */
public @interface HashType {
    HashLevel value() default HashLevel.NORMAL;

}

/**
 * 配合HashType注解
 * 标记HashCodeLib基础类库的各种hash的性能等级
 * <p>
 * 分为四个等级默认是NORMAL能级
 * HIGH
 * MIDDLE
 * NORMAL
 * LOW
 */
enum HashLevel {

    HIGH, MIDDLE, NORMAL, LOW
}

内存转换工具类

package top.boking.utils;

import top.boking.bloom.MemorySize;

/**
 * 存储空间换算工具类
 */
public class MemoryUtils {

    /**
     * 输入地址位数,换算成可视化的存储空间大小返回
     *
     * @param adnum
     * @return
     */
    public static String getMemorySize(int adnum) {
        if(adnum<=3){
            return "to small";
        }
        String dw = "B";
        for (int j = 0; j < 5; j++) {
            if (j == 0) {
                dw = "B";
            }
            if (j == 1) {
                dw = "KB";
            }
            if (j == 2) {
                dw = "MB";
            }
            if (j == 3) {
                dw = "GB";
            }
            if (j == 4) {
                dw = "TB";
            }
            for (int i = 0; i < 10; i++) {
                if (adnum == (j * 10) + i + 3) {
                    int w = 1 << i;
                    return w + dw;
                }
            }
        }
        return "error";

    }

    /**
     * 已废弃
     *
     * @param ms
     * @return
     */
    @Deprecated
    public static int getNumberSize(MemorySize ms) {
        switch (ms) {
            case MB64:
                return 29;
            case MB128:
                return 30;

            case MB256:
                return 31;
            case MB512:
                return 32;
            case GB:
                return 33;
            case GB4:
                return 34;

            case GB8:
                return 35;
        }
        return -1;
    }

    /**
     * 输入存储的位数,输出可视化的存储空间大小返回
     * (位数只能是2指数)
     *
     * @param l
     * @return
     */
    public static String inBitOutSize(long l) {
        long copy = l;
        if (l == 0) {
            return "input is zero";
        }

        int size = 0, left = 0;
        while ((l & 1) == 0) {
            l >>>= 1;
            size++;
        }
        while ((copy & Long.MIN_VALUE) == 0) {
            copy <<= 1;
            left++;
        }
        if (left + size == 63)
            return getMemorySize(size);
        return "input formatter error";
    }

    /**
     * 判断输入值是否是2的指数
     * @param num
     * @return
     */
    private static boolean isFacByTwo(long num) {
        if (num == 0) {
            return false;
        }
        long copy = num;
        int left = 0, right = 0;
        while ((num & 1) == 0) {
            num >>>= 1;
            left++;
        }
        while ((copy & Long.MIN_VALUE) == 0) {
            copy <<= 1;
            right++;
        }

        return 63 == (right + left);
    }
    
    public static void main(String[] args) {

        String s = inBitOutSize(1L << 33);
        System.out.println("s = " + s);


    }

}

标签:return,int,布隆,static,key,过滤器,java,public,hash
来源: https://blog.csdn.net/vay_ee/article/details/121324372

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有