使用二進制解決一個字段代表多個狀態的問題

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

結果如下: 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章