Java 位域

Java位域


這個概念是在 Effective Java中瞭解到的, 能夠經過EnumSet來代替位域這種方式表達.
java

並非很常見的概念, 所以記錄下.算法

若是在這以前剛好了解過 bitmap這種數據結構就更好了。安全

不瞭解也沒有關係。數據結構

bitmap 就是用bit的每一位來表明一個特殊的狀態值, 或者說標籤屬性等等.性能

舉例來講, 8位的數值, 用 0000 0001 表明 北, 0000 0010 表明南, 0000 0100 表明西 依次類推.this

那麼當咱們拿到一串bit, 如:code

0100 0000 天然能夠去對應的映射關係表中查找到 到底是屬於哪種類型, 若是咱們想同時傳遞兩個數值呢?接口

只須要 0000 0011 這樣就能夠表示 北 南 兩個方向了, 固然 至多能夠表示 8個方向.內存

咱們來試試這種表示方式:element

public class Direction {

    public static final short NORTH = 1;

    public static final short SOUTH = 1 << 2;

    public static final short WEST = 1 << 3;

    public static final short EAST = 1 << 4;

    public static final short SOUTH_EAST = 1 << 8;
}

在這裏我只是簡略的定義了其中5中.

那麼可能會有一個問題, 既然使用 short來表示, 爲何不用 1 2 3 ... 8 來表示數據呢? 這樣咱們甚至都不須要2 的 8次方, 只須要 3位就可以表示全部數據了.

可是不妨讓咱們再來想想, 在使用 1 ~ 8 的方式中如何同時傳入多種狀態呢?

在這裏是否是必須使用 一個 short[] 去接收數據?

那麼用位有什麼好處呢?

void array(NORTH | SOUTH | SOUTH_EAST)

在方法的調用上 能夠採用這種直觀易懂且計算速度快的方式, 而在傳入值 不難發現 最終只有一個值:

1000 0011

這一個數值即表示了包含了相應的三種狀態.

而這就是 java中 位域的使用方式.

那麼進一步來看, 當咱們再也不知足 8位 甚至須要更多種狀態值的時候 能夠切換到 int long 甚至於 bitmap. 接收無限位。

但僅僅是位域這種表示 咱們僅僅支持 64種如下的狀態類型, 由於 java種最長的基本類型 也就只有64位了。

那麼繼續來看看這種位域有什麼缺陷呢?

使用int 類型 或 long類型, 沒有辦法加入一些自定義的東西, 一般狀況下 在這種地方使用枚舉是更好地選擇。

不然的話全部的地方依然要使用 switch判斷的方式, 另外因爲 int定義爲 static final 時 自己就是編譯時常量,

若是有人依賴他, 未來即便這裏的數值更新了, 好比刪掉兩三個, 即便不從新編譯, 對方的class文件依然不會出錯。 但事實上, 出錯是一種必然。

就上面的例子來講, 咱們想要返回全部的String 該怎麼辦?

必然是 switch case return "南" 相似的方式.

那麼切換成枚舉類型呢?

public enum EnumDirection {

    NORTH("north"), EAST("east"), SOUTH("south"), WEST("south");

    private final String name;

    private EnumDirection(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

枚舉類型的好處,再也不贅述。

那與今天的主題, 位域有什麼關係呢?

咱們知道,位域的優勢 佔用內存小, 表示方便, 傳遞值方便, 性能高.

EnumSet, 讓我抄一段描述:

這個類實現Set接口,提供了豐富的功能,類型安全性,以及能夠從任何其餘Set實現中獲得的互用性。可是在內部具體的實現上,每一個EnumSet內容都表示爲位矢量。若是底層的枚舉類型有64個或者更少的元素——大多數如此。整個EnumSet就用單個long來表示,所以它的性能比的上位域的性能。批處理,如removeAll和retainAll,都是利用位算法來實現的。就像手工替代位域實現得那樣。

是的, 是位運算.

就看一段代碼:

public boolean contains(Object e) {
    if (e == null)
        return false;
    Class<?> eClass = e.getClass();
    if (eClass != elementType && eClass.getSuperclass() != elementType)
        return false;

    return (elements & (1L << ((Enum<?>)e).ordinal())) != 0;
}

咱們關注到最後一行, 如上述EnumDirection, WEST 的 ordinal() 便是4, 也就意味着 它值在這裏被理解爲 1 << 4

而經過 elements 傳入enumSet 的集合, 如:

EnumSet<EnumDirection> enumSet = EnumSet.of(EnumDirection.EAST, EnumDirection.NORTH);

不難獲知 enumSet 的 elements值爲 0000 0011 固然 這裏是 long類型, 我只寫了最後8位, 而

0000 0011 & 0000 1000 必然是等於 0的 所以 contains 返回false.

這是極其高效的方式. 而目的也正在於解決 int型 位域的種種弊端.

相關文章
相關標籤/搜索