如果一個枚舉類型的元素主要用在集合中吟宦,一般就使用int枚舉模式,將2的不同倍數(shù)賦予每個常量:
public class Text {
public static final int STYLE_BOLD = 1<<0; // 1
public static final int STYLE_ITALIC = 1<<1; // 2
public static final int STYLE_UNDERLINE = 1<<2; // 4
public static final int STYLE_STRIKETHROUGH = 1<<3; // 8
// Parameter is bitwise OR of zero or more STYLE_constants
public void applyStyles(int styles) {.....}
}
這種表示法讓你用OR位運算將幾個常量合并到一個集合中,稱作位域
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
位域表示法也允許利用位操作猪落,有效地執(zhí)行像union(聯(lián)合)和intersection(交集)這樣的集合操作吕晌。但位域有著int枚舉常量的所有缺點铃诬,甚至更多蘸际。當位域以數(shù)字形式打印時座哩,翻譯位域比翻譯簡單的int枚舉常量要困難得多徒扶。甚至粮彤,要遍歷位域表示的所有元素也沒有很容易的方法。
什么是位域姜骡?
位域是指信息在存儲時导坟,并不需要占用一個完整的字節(jié),而只需占幾個或一個二進制位圈澈。
例如在存放一個開關(guān)量時惫周,只有0和1 兩種狀態(tài), 用一位二進位即可康栈。
為了節(jié)省存儲空間递递,并使處理簡便,C語言又提供了一種數(shù)據(jù)結(jié)構(gòu)啥么,稱為“位域”或“位段”登舞。
所謂“位域”是把一個字節(jié)中的二進位劃分為幾 個不同的區(qū)域,并說明每個區(qū)域的位數(shù)悬荣。
每個域有一個域名菠秒,允許在程序中按域名進行操作。這樣就可以把幾個不同的對象用一個字節(jié)的二進制位域來表示氯迂。
Java提供的位運算符有:左移( << )践叠、右移( >> ) 、位與( & ) 嚼蚀、位或( | )禁灼、位非( ~ )、位異或( ^ )轿曙,除了位非( ~ )是一元操作符外弄捕,其它的都是二元操作符哮独。
1、左移( << )
將二進制數(shù)值整體向左移位察藐。如:5<<2代表將5向左移2位:
package com.xcy;
public class Test {
public static void main(String[] args) {
System.out.println(5<<2);
}
}
運行結(jié)果是20皮璧,但是程序是怎樣執(zhí)行的呢?
首先會將5轉(zhuǎn)為2進制表示形式(java中分飞,整數(shù)默認就是int類型,也就是32位):
0000 0000 0000 0000 0000 0000 0000 0101 然后左移2位后悴务,低位補0:
0000 0000 0000 0000 0000 0000 0001 0100 換算成10進制為20
2、右移( >> ) 譬猫,右移同理讯檐,只是方向不一樣罷了
System.out.println(5>>2);
//運行結(jié)果是1
還是先將5轉(zhuǎn)為2進制表示形式:
0000 0000 0000 0000 0000 0000 0000 0101 然后右移2位,高位補0:
0000 0000 0000 0000 0000 0000 0000 0001
3染服、位與( & )
位與:第一個操作數(shù)的的第n位于第二個操作數(shù)的第n位如果都是1别洪,那么結(jié)果的第n為也為1,否則為0
package com.xcy;
public class Test {
public static void main(String[] args) {
System.out.println(5 & 3);
}
}
//運行結(jié)果是1
將2個操作數(shù)和結(jié)果都轉(zhuǎn)換為二進制進行比較:
5轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0101
3轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0011
1轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0001
4柳刮、位或( | )
位或操作:第一個操作數(shù)的的第n位于第二個操作數(shù)的第n位 只要有一個是1挖垛,那么結(jié)果的第n為也為1,否則為0
package com.xcy;
public class Test {
public static void main(String[] args) {
System.out.println(5 | 3);
}
}
//結(jié)果為7
5轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0101
3轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0011
7轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0111
5秉颗、位異或( ^ )
位異或:第一個操作數(shù)的的第n位于第二個操作數(shù)的第n位 相反痢毒,那么結(jié)果的第n為也為1,否則為0
package com.xcy;
public class Test {
public static void main(String[] args) {
System.out.println(5 ^ 3);
}
}
//結(jié)果為6
5轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0101
3轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0011
6轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0110
6蚕甥、位非( ~ ) 位非是一元操作符
位非:操作數(shù)的第n位為1哪替,那么結(jié)果的第n位為0,反之菇怀。
package com.xcy;
public class Test {
public static void main(String[] args) {
System.out.println(~5);
}
}
//結(jié)果為-6
5轉(zhuǎn)換為二進制:0000 0000 0000 0000 0000 0000 0000 0101
-6轉(zhuǎn)換為二進制:1111 1111 1111 1111 1111 1111 1111 1010
由位運算操作符衍生而來的有:
&= 按位與賦值
|= 按位或賦值
^= 按位非賦值
= 右移賦值
<<= 賦值左移
和 += 一個概念而已凭舶。
舉個例子:
package com.xcy;
public class Test {
public static void main(String[] args) {
int a = 5
a &= 3;
System.out.println(a);//結(jié)果是1
}
}
因為位運算的運算效率比直接對數(shù)字進行加減乘除高很多,所以當出現(xiàn)以下情景且對運算效率要求較高時爱沟,可以考慮使用位運算帅霜。不過實際工作中,很少用到它钥顽,我也不知道為什么很少有人用它义屏,我想應(yīng)該是它比較晦澀難懂,如果用它來進行一些運算蜂大,估計編寫的代碼的可讀性會不強闽铐,畢竟我們寫的代碼不僅僅留給自己一個人看。
- 判斷int型變量a是奇數(shù)還是偶數(shù)
a&1 = 0 偶數(shù)
a&1 = 1 奇數(shù) - 求平均值奶浦,比如有兩個int類型變量x兄墅、y,首先要求x+y的和,再除以2澳叉,但是有可能x+y的結(jié)果會超過int的最大表示范圍隙咸,所以位運算就派上用場啦沐悦。
(x&y)+((x^y)>>1); - 對于一個大于0的整數(shù),判斷它是不是2的幾次方
((x&(x-1))==0)&&(x!=0)五督; - 比如有兩個int類型變量x藏否、y,要求兩者數(shù)字交換,位運算的實現(xiàn)方法:性能絕對高效
x ^= y;
y ^= x;
x ^= y; - 求絕對值
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
} - 取模運算充包,采用位運算實現(xiàn):
a % (2^n) 等價于 a & (2^n - 1) - 乘法運算 采用位運算實現(xiàn)
a * (2^n) 等價于 a << n - 除法運算轉(zhuǎn)化成位運算
a / (2^n) 等價于 a>> n - 求相反數(shù)
(~x+1)
10 a % 2 等價于 a & 1
對于需要傳遞對組常量集時副签,可以使用EnumSet來代替位域,EnumSet有效的表示從單個枚舉類型中提取的多個值的多個集合.這個類實現(xiàn)Set接口,提供了豐富的功能和類型安全性,以及可以從任何其他Set實現(xiàn)中得到的互用性.
下面用EnumSet將2的不同倍數(shù)賦值給每個常量
public class Text{
public enum Style{BOLD,ITALIC,UNDERLINE,STRIKETHROUGH}
public void applyStyles(Set<Style> styles){
//實現(xiàn)方案
}
}
客戶端代碼
text.applyStyles(EnumSet.of(Style.BOLD,Style.ITALIC));
總之:正是因為枚舉類型要用在集合(Set)中,所以沒有理由用位域來表示它.EnumSet類集位域的簡潔和性能的優(yōu)勢及枚舉類型的所有優(yōu)點與一身.實際上EnumSet也有個缺點,即它無法創(chuàng)建不可變的EnumSet(Java1.6為止沒有解決).同時,可以用Collections.unmodifiable將EnumSet封裝起來,但是間接性和性能會受到影響.
http://blog.csdn.net/hudashi/article/details/6943843 EnumSet 使用
http://brokendreams.iteye.com/blog/2267485 Jdk1.6 Collections Framework源碼解析(11)-EnumSet