java – 正确使用位掩码?

前端之家收集整理的这篇文章主要介绍了java – 正确使用位掩码?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
heyhey,
只是对bitmasks有疑问.我想我现在知道它们是什么以及它们可以在哪里使用.
我想存储特定权限,如BUILD,BREAK和INTERACT,以及更多特定群组.下面的代码应该这样做,但我不太确定这是否是正确的“风格”.

这个想法是在这里使用前3位来存储第一组的权限,然后使用接下来的三位用于第二组,依此类推.
所以我现在的问题是这是一种好方法还是更好的方法

public class Test {
    private int permissions = 0;

    /**
     * The amount of permissions,currently: {@link #BREAK},{@link #BUILD},{@link #INTERACT}
     */
    private static final int PERMISSIONS = 3;
    /**
     * The different permissions
     */
    public static final int BUILD = 1,BREAK = 2,INTERACT = 4;
    /**
     * The different groups
     */
    public static final int ALLIANCE = 0,OUTSIDERS = 1;

    public void setPermissions(int permissions,int group)
    {
        this.permissions = permissions << group * PERMISSIONS;
    }

    public void addPermissions(int permission,int group)
    {
        setPermissions(this.permissions | permission,group);
    }

    public boolean hasPermission(int permission,int group)
    {
        return (permissions & permission << group * PERMISSIONS) == permission;
    }
}

编辑:我想尽可能少使用内存,因为我需要存储大量数据.

编辑:我还需要将它存储在一个sql数据库中,但它不应该生成probs.

解决方法

你知道这种答案迟早要出现,所以在这里:

虽然位掩码的使用可以说是最快的并且具有所有替代选项的最低内存消耗,但它也非常容易出错并且除了在一些非常边缘的情况下使用它之外大多不鼓励使用.这是一款经典的低级工具.如果做得好,作品奇迹,如果误用,可能会造成严重破坏.

因此,正确的方法是使用更高级别的抽象,即枚举和EnumSets.速度和记忆的消耗是可比的,当然稍微差一点.但在一般情况下,它们绝对足够了.根据您的具体情况和需求,有很多方法可以做到这一点.其中一种可能性是:

public enum Permission {
    BUILD,BREAK,INTERACT;
}

public class Permissions {
    private final Set<Permission> alliance = EnumSet.noneOf(Permission.class);
    private final Set<Permission> outsiders = EnumSet.noneOf(Permission.class);

    public Set<Permission> alliance() {
        return alliance;
    }

    public Set<Permission> outsiders() {
        return outsiders;
    }
}

仅此一项就可以让您完成您所做的事情,但有两点不同:

>我认为现在它是类型安全的,更加万无一失.不需要重新发明轮子.
>它使用更多内存.不是很多,因为EnumSet这个小通常只是一个很长的.

编辑以回答OP关于将EnumSet存储到数据库的注释:

是的,这可能是一个问题,因为存储int非常容易.如果你仍然认为坚持使用EnumSet,那么从我的头脑中有几种可能性:

> Look at SO.人们之前曾试图解决这个问题.
>保存EnumSet中值的名称

Permissions p = new Permissions();
p.alliance().addAll(EnumSet.of(Permission.BREAK,Permission.BUILD));
for (Permission permission : p.alliance()) {
    System.out.println(permission);
}

然后,您可以轻松地重建值:

for (String word : stringsFromDtb) {
    p.alliance.add(Permission.valueOf(word));
}

>保存序数.这非常危险,因为你可以通过更改Permission枚举来轻松破解它.此外,任何随机数都可以输入以打破这一点.

Permissions p = new Permissions();
p.alliance().addAll(EnumSet.of(Permission.BREAK,Permission.BUILD));
for (Permission permission : p.alliance()) {
    System.out.println(permission.ordinal());
}

然后,您可以轻松地重建值:

for (int ordinal : ordinalsFromDtb) {
    p.alliance.add(Permission.values()[ordinal]);
}

>以通常的方式序列化EnumSet并直接存储二进制数据或BASE64ed.埃姆.

编辑后编辑:

广告.您为枚举值制作索引的注释,以便在将来更改或重新排序时,它仍然有效.有一个简单的方法来使用枚举!它基本上是位域和枚举之间的中间路径,它保留了类型安全性和所有枚举功能,并且仍然具有位域的优点.

public enum Permission {
    /* I like to have binary literals in place of bit fields,* but any notation will work */
    BUILD   (0b0001),BREAK   (0b0010),INTERACT(0b0100);

    private final int index;

    private Permission(int index) {
        this.index = index;
    }

    public int index() {
        return index;
    }
}

然后,您将索引保存到数据库中,只需要确保从中解析是正确的.此外,将来只会注释掉(而不是删除)任何不需要的枚举值,这样它们仍然可以为您显示,并且您不会占用它的索引.或者只是将其标记为@Deprecated并且您不必删除任何内容;).

猜你在找的Java相关文章