Java位運算在程式設計中的使用:位掩碼(BitMask)
在Java中,位運算子有很多,例如與(&)、非(~)、或(|)、異或(^)、移位(<<和>>)等。這些運算子在日常編碼中很少會用到。
在下面的一個例子中,會用到位掩碼(BitMask),其中包含大量的位運算。不只是在Java中,其他編寫語言中也是可以使用的。
例如,在一個系統中,使用者一般有查詢(Select)、新增(Insert)、修改(Update)、刪除(Selete)四種許可權,四種許可權有多種組合方式,也就是有16中不同的許可權狀態(2的4次方)。
一般情況下會想到用四個boolean型別變數來儲存:
Permission.java
public class Permission { // 是否允許查詢 private boolean allowSelect; // 是否允許新增 private boolean allowInsert; // 是否允許刪除 private boolean allowDelete; // 是否允許更新 private boolean allowUpdate; public boolean isAllowSelect() { return allowSelect; } public void setAllowSelect(boolean allowSelect) { this.allowSelect = allowSelect; } public boolean isAllowInsert() { return allowInsert; } public void setAllowInsert(boolean allowInsert) { this.allowInsert = allowInsert; } public boolean isAllowDelete() { return allowDelete; } public void setAllowDelete(boolean allowDelete) { this.allowDelete = allowDelete; } public boolean isAllowUpdate() { return allowUpdate; } public void setAllowUpdate(boolean allowUpdate) { this.allowUpdate = allowUpdate; } }
上面用四個boolean型別變數來儲存每種許可權狀態。下面是另外一種方式,使用位掩碼的話,用一個二進位制數即可,每一位來表示一種許可權,0表示無許可權,1表示有許可權。
NewPermission.java
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; } }
NewPermission程式碼中,用四個常量表示了每個二進位制位程式碼的許可權項。
例如:
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);
其他方法不再一一解釋。下面對比Permission和NewPermission兩種方式:
1.設定僅允許Select和Insert許可權
Permission
permission.setAllowSelect(true);
permission.setAllowInsert(true);
permission.setAllowUpdate(false);
permission.setAllowDelete(false);
NewPermissionpermission.setPermission(NewPermission.ALLOW_SELECT | NewPermission.ALLOW_INSERT);
2.判斷是否允許Select和Insert、Update許可權Permission
if (permission.isAllowSelect() && permission.isAllowInsert() && permission.isAllowUpdate())
NewPermission
if (permission. isAllow (NewPermission.ALLOW_SELECT
| NewPermission.ALLOW_INSERT | NewPermission.ALLOW_UPDATE))
3.判斷是隻否允許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反射中,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;
}
這裡使用二進位制位來表示是否有某個修飾符。具體使用方式可以去看我的另一篇博文:Java反射之如何判斷類或變數、方法的修飾符(Modifier解析)此外,在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;
}
作者:叉叉哥 轉載請註明出處:http://blog.csdn.net/xiao__gui/article/details/11701893