使用二进制解决一个字段代表多个状态的问题

package com.king.common.utils.binary;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 使用二进制解决一个字段代表多个状态的问题
 *
 * 有没有更好的方法,处理一条记录多个状态的问题呢?
 * 使用二进制位,每个位置,代表一个状态,多个位置代表多个状态,存储的时候转换为int类型存储。
 *
 * 以产品属性存储为例。
 * 热门:0000 0001
 * 新品:0000 0010
 * 特卖:0000 0100
 *
 * 多个状态的组合
 * 热门+新品:0000 0011
 * 热门+特卖:0000 0101
 * 热门+新品+特卖:0000 0111
 *
 * 存储时将二进制转换为int数值。
 *
 * 二进制位的查询
 * 以Oracle为例,使用bitadd函数,如:
 *     SELECT * FROM T_PRODUCT WHERE bitand(property,1)=1
 * 以Mysql为例,使用&运算符,如:
 *     SELECT data_code,data_value FROM `tb_ceshi_data` where ceshi_code='xxx' and data_value & 8
 * java中对二进制位的处理
 * 1)是否包含:&与运算符,比如:(1&3)==1
 * 2)组装:|或运算符,比如 1|2=3
 *
 * 如果再扩展,清仓状态,可以使用 0000 1000
 *
 * 二进制实现的好处
 * 以上内容讲解了使用二进制位来表示状态的方式,实现数据库设计和程序调用。有以下好处
 * 1)存储精简
 * 2)扩展性强
 *
 * 缺点:
 * 1)增加了理解复杂度;
 *
 * 以下是六个状态情况的二进制与十进制存储对照表:
 * 00000001 1  参数1
 * 00000010 2  参数2
 * 00000100 4  参数3
 * 00001000 8  参数4
 * 00010000 16 参数5
 * 00100000 32 参数6
 *
 * 00000011 3  参数1+参数2
 * 00000101 5  参数1+参数3
 * 00000110 6  参数2+参数3
 * 00000111 7  参数1+参数2+参数3
 * 00001001 9  参数1+参数4
 * 00001011 11 参数1+参数2+参数4
 * 00001101 13 参数1+参数3+参数4
 * 00001111 15 参数1+参数2+参数3+参数4
 * 00010110 22 参数2+参数3+参数5
 * 00111111 63 参数1+参数2+参数3+参数4+参数5+参数6
 *
 *---------------------------------------java中操作二进制的运算符总结(&,| , ^, ~, , >>> )-----------------------------------------
 * &运算符总结
 * 1.特点:二元操作符,操作两个二进制数据;两个二进制数最低位对齐,只有当两个对位数都是1时才为1,否则为0
 * 2.案例:
 *    int a = 3 & 2 ;
 *    System.out.println(a); //结果为 2
 * 3.分析:
 *    3的二进制补码表示为:
 *        00000000 00000000 00000000 00000011
 *    2的二进制补码表示为:
 *        00000000 00000000 00000000 00000010
 *    运算:3 & 2
 *        00000000 00000000 00000000 00000011
 *    &   00000000 00000000 00000000 00000010
 *    -------------------------------------------
 *        00000000 00000000 00000000 00000010            二进制是2
 *
 * @Description
 * @Author HHJ
 * @Date 2019-05-20 16:14
 */
public class BinaryUtil {
    private static final Logger log = LoggerFactory.getLogger(BinaryUtil.class);

    /**
     * 求list数值之和(list="1,2,4,8,16,32....")
     * (使用场景是前端将list传入进来.后端计算list之和,然后将结果直接入库)
     *
     * @param list
     * @return
     */
    public static Integer sum(List<Integer> list) {
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }
        return list.stream().reduce(Integer::sum).orElse(0);
    }

    /**
     * 二进制转换为十进制
     *(在这种方式下,该方法无用)
     * 
     * @param binaryString
     * @return
     */
    public static Integer binaryToInteger(String binaryString) {
        Integer i = Integer.parseInt(binaryString, 2);
        return i;
    }

    /**
     * 以base为底数.value为值,算对数.例如
     *     计算64以4为底的对数:
     *     double res = log(64, 4);//4*4*4=64
     *     Assert.isTrue(res==3)
     * 
     * @param value
     * @param base
     * @return
     */
    public static double log(double value, double base) {
        return Math.log(value) / Math.log(base);
    }

    /**
     * 十进制数值 转对应的list
     * (使用场景是将DB中对应的int转对应的list,然后将结果返回给前端)
     * 
     * @param data
     * @return
     */
    public static List<Integer> toList(Integer data) {
        List<Integer> list = new ArrayList<>();
        if (data <= 0) {
            return list;
        }
        double count = log((double) data, 2d);
        for (int i = 0; i < count + 1; i++) {
            int n = 1 << i;
            if ((data & n) == n) {
                list.add(n);
            }
        }
        return list;
    }

    public static void main(String[] args) {
        List<Integer> realList = toList(22);
        System.out.println(realList.toString());
        int integer = binaryToInteger("00001011");
        Assert.isTrue(11 == integer, "this expression must be true");

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(4);
        list.add(8);
        Integer sum = sum(list);
        System.out.println("sum=" + sum);
        List<Integer> realList1 = toList(sum );
        System.out.println(realList1.toString());
        int data = 34;
        List<Integer> result = toList(data);

        System.out.println("result.size:" + result.size() + "...结果:" + result.toString());
    }
}

 案例:

分别定义1,2,4,8,16,32表示六个选项的值,用户提交答案.入库数据如下:

现在欲查询选择了8选项的数据.mysql的查询语句如下:

SELECT data_code,data_value,bin(data_value) as 二进制 FROM `tb_ceshi_data` where data_value & 8

结果如下: 

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值