zoukankan      html  css  js  c++  java
  • Java位运算在程序设计中的使用:位掩码(BitMask)

    转自:http://xxgblog.com/2013/09/15/java-bitmask/

    在Java中,位运算符有很多,例如与(&)、非(~)、或(|)、异或(^)、移位(<<和>>)等。这些运算符在日常编码中很少会用到。

    在下面的一个例子中,会用到位掩码(BitMask),其中包含大量的位运算。不只是在Java中,其他编写语言中也是可以使用的。

    例如,在一个系统中,用户一般有查询(Select)、新增(Insert)、修改(Update)、删除(Delete)四种权限,四种权限有多种组合方式,也就是有16中不同的权限状态(2的4次方)。

    Permission

    一般情况下会想到用四个boolean类型变量来保存:

    public class Permission {
        
        // 是否允许查询
        private boolean allowSelect;
        
        // 是否允许新增
        private boolean allowInsert;
        
        // 是否允许删除
        private boolean allowDelete;
        
        // 是否允许更新
        private boolean allowUpdate;
    
        // 省略Getter和Setter
    }

    上面用四个boolean类型变量来保存每种权限状态。

    NewPermission

    下面是另外一种方式,使用位掩码的话,用一个二进制数即可,每一位来表示一种权限,0表示无权限,1表示有权限。

    public class NewPermission {
        // 是否允许查询,二进制第1位,0表示否,1表示是
        public static final int ALLOW_SELECT = 1 << 0; // 0001
        
        // 是否允许新增,二进制第2位,0表示否,1表示是
        public static final int ALLOW_INSERT = 1 << 1; // 0010
        
        // 是否允许修改,二进制第3位,0表示否,1表示是
        public static final int ALLOW_UPDATE = 1 << 2; // 0100
        
        // 是否允许删除,二进制第4位,0表示否,1表示是
        public static final int ALLOW_DELETE = 1 << 3; // 1000
        
        // 存储目前的权限状态
        private int flag;
    
        /**
         *  重新设置权限
         */
        public void setPermission(int permission) {
            flag = permission;
        }
    
        /**
         *  添加一项或多项权限
         */
        public void enable(int permission) {
            flag |= permission;
        }
        
        /**
         *  删除一项或多项权限
         */
        public void disable(int permission) {
            flag &= ~permission;
        }
        
        /**
         *  是否拥某些权限
         */
        public boolean isAllow(int permission) {
            return (flag & permission) == permission;
        }
        
        /**
         *  是否禁用了某些权限
         */
        public boolean isNotAllow(int permission) {
            return (flag & permission) == 0;
        }
        
        /**
         *  是否仅仅拥有某些权限
         */
        public boolean isOnlyAllow(int permission) {
            return flag == permission;
        }
    }

    以上代码中,用四个常量表示了每个二进制位代码的权限项。

    例如:

    ALLOW_SELECT = 1 << 0 转成二进制就是0001,二进制第一位表示Select权限。
    ALLOW_INSERT = 1 << 1 转成二进制就是0010,二进制第二位表示Insert权限。

    private int flag存储了各种权限的启用和停用状态,相当于代替了Permission中的四个boolean类型的变量。

    用flag的四个二进制位来表示四种权限的状态,每一位的0和1代表一项权限的启用和停用,下面列举了部分状态表示的权限:

    flag 删除 修改 新增 查询  
    1(0001) 0 0 0 1 只允许查询(即等于ALLOW_SELECT)
    2(0010) 0 0 1 0 只允许新增(即等于ALLOW_INSERT)
    4(0100) 0 1 0 0 只允许修改(即等于ALLOW_UPDATE)
    8(1000) 1 0 0 0 只允许删除(即等于ALLOW_DELETE)
    3(0011) 0 0 1 1 只允许查询和新增
    0 0 0 0 0 四项权限都不允许
    15(1111) 1 1 1 1 四项权限都允许

    使用位掩码的方式,只需要用一个大于或等于0且小于16的整数即可表示所有的16种权限的状态。

    此外,还有很多设置权限和判断权限的方法,需要用到位运算,例如:

    public void enable(int permission) {
        flag |= permission; // 相当于flag = flag | permission;
    }

    调用这个方法可以在现有的权限基础上添加一项或多项权限。

    添加一项Update权限:

    permission.enable(NewPermission.ALLOW_UPDATE);

    假设现有权限只有Select,也就是flag是0001。执行以上代码,flag = 0001 | 0100,也就是0101,便拥有了Select和Update两项权限。

    添加Insert、Update、Delete三项权限:

    permission.enable(NewPermission.ALLOW_INSERT 
        | NewPermission.ALLOW_UPDATE | NewPermission.ALLOW_DELETE);

    NewPermission.ALLOW_INSERT | NewPermission.ALLOW_UPDATE | NewPermission.ALLOW_DELETE运算结果是1110。假设现有权限只有Select,也就是flag是0001。flag = 0001 | 1110,也就是1111,便拥有了这四项权限,相当于添加了三项权限。

    上面的设置如果使用最初的Permission类的话,就需要下面三行代码:

    permission.setAllowInsert(true);
    permission.setAllowUpdate(true);
    permission.setAllowDelete(true);

    二者对比

    设置仅允许Select和Insert权限

    Permission

    permission.setAllowSelect(true);
    permission.setAllowInsert(true);
    permission.setAllowUpdate(false);
    permission.setAllowDelete(false);

    NewPermission

    permission.setPermission(NewPermission.ALLOW_SELECT | NewPermission.ALLOW_INSERT);

    判断是否允许Select和Insert、Update权限

    Permission

    if (permission.isAllowSelect() && permission.isAllowInsert() && permission.isAllowUpdate())

    NewPermission

    if (permission. isAllow (NewPermission.ALLOW_SELECT 
        | NewPermission.ALLOW_INSERT | NewPermission.ALLOW_UPDATE))

    判断是只否允许Select和Insert权限

    Permission

    if (permission.isAllowSelect() && permission.isAllowInsert() 
        && !permission.isAllowUpdate() && !permission.isAllowDelete())

    NewPermission

    if (permission. isOnlyAllow (NewPermission.ALLOW_SELECT | NewPermission.ALLOW_INSERT))

    二者对比可以感受到MyPermission位掩码方式相对于Permission的优势,可以节省很多代码量,位运算是底层运算,效率也非常高,而且理解起来也很简单。

    一些用到位掩码的源代码

    java.lang.reflect.Modifier

    在Java反射中,java.lang.reflect.Modifier是用来判断类、成员变量、方法等包含的修饰符。在Modifier的源代码中可以看到:

    public static final int PUBLIC           = 0x00000001;
    public static final int PRIVATE          = 0x00000002;  
    public static final int PROTECTED        = 0x00000004;  
    public static final int STATIC           = 0x00000008;  
    public static final int FINAL            = 0x00000010;  
    public static final int SYNCHRONIZED     = 0x00000020;
    // ......
    public static boolean isProtected(int mod) {
        return (mod & PROTECTED) != 0;
    }
    public static boolean isStatic(int mod) {
        return (mod & STATIC) != 0;
    }

    android.text.util.Linkify

    在Android开发中,Linkify可以设置文本中的地址、电话、邮箱等是否支持点击链接:

    Linkify.addLinks(textView, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES);

    android.text.util.Linkify部分源码:

    public static final int WEB_URLS = 0x01;
    public static final int EMAIL_ADDRESSES = 0x02;
    public static final int PHONE_NUMBERS = 0x04;
    public static final int MAP_ADDRESSES = 0x08;
    public static final int ALL = WEB_URLS | EMAIL_ADDRESSES | PHONE_NUMBERS | MAP_ADDRESSES;
    
    public static final boolean addLinks(Spannable text, int mask) {
        if (mask == 0) {
            return false;
        }
    
        URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
    
        for (int i = old.length - 1; i >= 0; i--) {
            text.removeSpan(old[i]);
        }
    
        ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
    
        if ((mask & WEB_URLS) != 0) {
            gatherLinks(links, text, Patterns.WEB_URL,
                new String[] { "http://", "https://", "rtsp://" },
                sUrlMatchFilter, null);
        }
    
        if ((mask & EMAIL_ADDRESSES) != 0) {
            gatherLinks(links, text, Patterns.EMAIL_ADDRESS,
                new String[] { "mailto:" },
                null, null);
        }
    
        if ((mask & PHONE_NUMBERS) != 0) {
            gatherLinks(links, text, Patterns.PHONE,
                new String[] { "tel:" },
                sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);
        }
    
        if ((mask & MAP_ADDRESSES) != 0) {
            gatherMapLinks(links, text);
        }
    
        pruneOverlaps(links);
    
        if (links.size() == 0) {
            return false;
        }
    
        for (LinkSpec link: links) {
            applyLink(link.url, link.start, link.end, text);
        }
    
        return true;
    }
  • 相关阅读:
    Civil 3D 二次开发 创建Civil 3D 对象—— 01 —— 创建几何空间点
    Civil 3D 二次开发 创建Civil 3D 对象—— 00 ——
    Civil 3D 二次开发 创建AutoCAD对象—— 01 —— 创建直线
    Civil 3D 二次开发 新建CLR项目出现错误C2143
    Civil 3D 二次开发 创建AutoCAD对象—— 00 ——
    了解AutoCAD对象层次结构 —— 6 ——块表记录
    datepicker97使用
    使用angular 外接 templateUrl,使用ng-include
    angularJs 遮罩
    网上找的有关css兼容问题
  • 原文地址:https://www.cnblogs.com/hihtml5/p/6483783.html
Copyright © 2011-2022 走看看