天天看點

不談源碼,聊聊位運算的實際應用

Hello,這裡是愛 Coding,愛 Hiphop,愛喝點小酒的 AKA 柏炎。

位運算是java中比較基礎的知識,位運算的計算速度很快,jdk中不少源碼都是使用位運算​

​(例如ArrayList,HashMap)​

​。

但是在日常開發的過程中,我們很少會使用位運算。相比較它的計算速度的優點,它可讀性不高的”缺點“似乎對于普通的業務開發來說更加突出。

我們更加樂意直接使用 ​

​+,-,*,/​

​這些直接的運算符。

本文将給大家介紹一種位運算在業務中的應用場景,希望能給大家帶來幫助,如有不對之處,歡迎指正。

本位不對位運算的基礎做展開介紹,如果不熟悉位運算的基礎概念的,請先自行google了解。
不談源碼,聊聊位運算的實際應用

一、正常業務打标思路

日常業務開發的過程中,經常會有對業務模型打标 (true/false) 的需求。

例如:

true表示男生,false表示女生

true表示參與活動優惠,false表示無法參與活動優惠

true表示超級管理者,false表示普通使用者

.....

一般情況下針對這種需求,我們通常會使用一個獨立的字段去表示Domain的狀态。随着業務的發展,需要打标的邏輯越來越多。對應到Domain/表結構中辨別的字段也會不斷的上升。

@Data
public class User {
    private Long userId;
    private String userName;
    private boolean canLogin;
    private boolean isMale;
    private boolean isAdmin;
    ....
}      

二、位運算優化邏輯

為了解決這種非A即B的字段辨別的膨脹問題,我們可以使用位運算去整合這些字段。

例如上文展示的User Domain,我們整合canLogin,isMale,isAdmin為一個字段,認為是一個feature辨別(設定字段為​

​featureTag​

​)。

@Data
public class User {
    private Long userId;
    private String userName;
    private int featureTag;
}      

我們可以定義一個feature的枚舉定義每一位辨別什麼

public enum UserFeatureEnum{

    CAN_LOGIN(0),
    IS_MALE(1);
    IS_ADMIN(2);

    private int bit;

    UserFeatureEnum(int bit) {
        this.bit = bit;
    }
    
    public int getValue() {
        return this.bit;
    }

}      

然後我們可以通過一下的BitUtil來check目前使用者某個特性的狀态

public class BitUtil {
    public static int setBit(int options, int bitIndex) {
        int val = 1 << bitIndex;
        return options | val;
    }

    public static int unsetBit(int options, int bitIndex) {
        int val = 1 << bitIndex;
        return options & (~ val);
    }

    public static boolean isBitSetted(int options, int bitIndex) {
        int val = 1 << bitIndex;
        return (options & val) == val;
    }

    public static boolean isBitUnSetted(int options, int bitIndex) {
        int val = 1 << bitIndex;
        return (options & val) == 0;
    }
}      

這樣我們如果去校驗user是否為男性就可以這麼操作

boolean isMale = BitUtil.isBitSetted(user.getfeatureTag(),UserFeatureEnum.IS_MALE.getValue());      

設定目前使用者為女性

user.setfeatureTag(BitUtil.unsetBit(user.getfeatureTag(),UserFeatureEnum.IS_MALE.getValue()))      

設定目前使用者為男性

user.setfeatureTag(BitUtil.setBit(user.getfeatureTag(),UserFeatureEnum.IS_MALE.getValue()))      

這樣我們将日益膨脹的标志字段給合并了,資料庫也能夠隻用一個字段就能夠辨別Domain的多個feature。

後續如果需要增加一個feature時,就不需要修改表結構了,隻需要增加一個枚舉值及設定、判斷邏輯就行。

像線上項目,改個表結構還要走一道道的DBA審批流程

三、優劣勢對比

  • 相比較正常的業務設計,位運算在空間節省與擴充性上更加突出,但是可讀性就比較差。
  • 在DB檢索上,正常設計可以直接将字段作為條件進行過濾,位運算需要在條件上進行一下位運算。
  • 在ES檢索上,正常設計可以直接将字段作為條件進行過濾,位運算就不支援了,需要備援出一個字段,如果Domain的存儲結構是ES,并且feature字段有檢索需求,那還是按照正常設計比較好。
  • 如果涉及到與用戶端互動的功能,版本A的client支援的功能,在版本B就不能支援了。由于用戶端是安裝在使用者本地的,不可能一直強制使用者做更新。是以這種時候可以在使用一個feature字段,辨別目前用戶端支援的一些特性,服務端去解析傳回結果。請求參數清晰簡單,動态性比較高。
不談源碼,聊聊位運算的實際應用

四、最後

文中如有不正确之處,歡迎指正,寫文不易,點個贊吧,麼麼哒~

微信:baiyan_lou

公衆号:柏炎大叔