第32條 用EnumSet代替位域

如果一個枚舉類型的元素主要用在集合中吟宦,一般就使用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)該是它比較晦澀難懂,如果用它來進行一些運算蜂大,估計編寫的代碼的可讀性會不強闽铐,畢竟我們寫的代碼不僅僅留給自己一個人看。

  1. 判斷int型變量a是奇數(shù)還是偶數(shù)
    a&1 = 0 偶數(shù)
    a&1 = 1 奇數(shù)
  2. 求平均值奶浦,比如有兩個int類型變量x兄墅、y,首先要求x+y的和,再除以2澳叉,但是有可能x+y的結(jié)果會超過int的最大表示范圍隙咸,所以位運算就派上用場啦沐悦。
    (x&y)+((x^y)>>1);
  3. 對于一個大于0的整數(shù),判斷它是不是2的幾次方
    ((x&(x-1))==0)&&(x!=0)五督;
  4. 比如有兩個int類型變量x藏否、y,要求兩者數(shù)字交換,位運算的實現(xiàn)方法:性能絕對高效
    x ^= y;
    y ^= x;
    x ^= y;
  5. 求絕對值
    int abs( int x )
    {
    int y ;
    y = x >> 31 ;
    return (x^y)-y ; //or: (x+y)^y
    }
  6. 取模運算充包,采用位運算實現(xiàn):
    a % (2^n) 等價于 a & (2^n - 1)
  7. 乘法運算 采用位運算實現(xiàn)
    a * (2^n) 等價于 a << n
  8. 除法運算轉(zhuǎn)化成位運算
    a / (2^n) 等價于 a>> n
  9. 求相反數(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末基矮,一起剝皮案震驚了整個濱河市淆储,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌家浇,老刑警劉巖本砰,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钢悲,居然都是意外死亡点额,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門譬巫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咖楣,“玉大人,你說我怎么就攤上這事芦昔。” “怎么了娃肿?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵咕缎,是天一觀的道長。 經(jīng)常有香客問我料扰,道長凭豪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任晒杈,我火速辦了婚禮嫂伞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拯钻。我一直安慰自己帖努,他們只是感情好,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布粪般。 她就那樣靜靜地躺著拼余,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亩歹。 梳的紋絲不亂的頭發(fā)上匙监,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天凡橱,我揣著相機與錄音,去河邊找鬼亭姥。 笑死稼钩,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的达罗。 我是一名探鬼主播变抽,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼氮块!你這毒婦竟也來了绍载?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤滔蝉,失蹤者是張志新(化名)和其女友劉穎击儡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝠引,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡阳谍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了螃概。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矫夯。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吊洼,靈堂內(nèi)的尸體忽然破棺而出训貌,到底是詐尸還是另有隱情,我是刑警寧澤冒窍,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布递沪,位于F島的核電站,受9級特大地震影響综液,放射性物質(zhì)發(fā)生泄漏款慨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一谬莹、第九天 我趴在偏房一處隱蔽的房頂上張望檩奠。 院中可真熱鬧,春花似錦附帽、人聲如沸埠戳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乞而。三九已至,卻和暖如春慢显,著一層夾襖步出監(jiān)牢的瞬間爪模,已是汗流浹背欠啤。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留屋灌,地道東北人洁段。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像共郭,于是被迫代替她去往敵國和親祠丝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android在編碼的時候經(jīng)常使用到位運算除嘹,這里以Intent的Flags為例写半。(查看Intent說明文檔) 首先...
    離人歌閱讀 1,874評論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)尉咕,斷路器叠蝇,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 1 關(guān)鍵字 1.1 關(guān)鍵字的概述 Java的關(guān)鍵字對java的編譯器有特殊的意義,他們用來表示一種數(shù)據(jù)類型年缎,或...
    哈哈哎呦喂閱讀 646評論 0 0
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法悔捶,類相關(guān)的語法,內(nèi)部類的語法单芜,繼承相關(guān)的語法蜕该,異常的語法,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 2006年洲鸠,作為一名高二學生堂淡,我開始接觸各種小說,彼時大家總是以憂郁來標榜自己酷的品格坛怪,所以看的閑書也是以類似的題...
    青樹芽閱讀 422評論 2 0