8

ちょっと、ビットマスクについて質問があります。私は今、それらが何であり、どこで使用できるかを知っていると思います。BUILD、BREAK、INTERACTなどの特定の権限を保存したいのですが、特定のグループにはもっと多くの権限を保存したいと思います。以下のコードはこれを行う必要がありますが、これが現在の「スタイル」であるかどうかはよくわかりません。

ここで最初の3ビットを使用して最初のグループのアクセス許可を格納し、次の3ビットを2番目のグループに使用します。だから私の質問は、これが良い方法であるかどうか、または何がより良いでしょうか?

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データベースに保存する必要がありますが、それは問題を起こさないはずです。

4

2 に答える 2

12

あなたはこの種の答えが遅かれ早かれ現れる必要があることを知っていたので、ここにそれが行きます:

ビットマスクの使用は間違いなく最速であり、すべての代替オプションの中でメモリ消費量が最も少ないですが、エラーが発生しやすく、一部の非常にエッジのある場合を除いて、ほとんど使用しないことをお勧めします。これは古典的な低レベルのツールです。正しく行われた場合、誤用された場合、作品は大混乱を引き起こす可能性があります。

したがって、正しいアプローチは、これに高レベルの抽象化、つまりenumsとを使用することですEnumSets。もちろん、速度とメモリ消費量はわずかに劣りますが、同等です。ただし、一般的なケースでは、それらで十分です。あなたの正確な文脈とニーズに基づいてそれを行う方法はたくさんあります。可能性の1つはこれである可能性があります:

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;
    }
}

これだけで、あなたがしたことを正確に行うことができますが、2つの違いがあります。

  1. 今ではタイプセーフで、より確実なものになっていると思います。ホイールの再発明は必要ありません。
  2. より多くのメモリを使用します。EnumSetこれは通常、単なる。であるため、それほど多くはありませんlong


EnumSetをデータベースに保存することに関するOPのコメントに答えるために編集します。

はい、保存は非常にint簡単なので、これは問題になる可能性があります。それでも、を使用することを検討している場合EnumSetは、頭のてっぺんからいくつかの可能性があります。

  1. SOを見てください。人々は以前にこの問題に取り組んだことがあります。

  2. 値の名前を:に保存し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));
    }
    
  3. 序数を保存します。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]);
    }
    
  4. 通常の方法でシリアルEnumSet化し、バイナリデータを直接またはBASE64で保存します。えーと。

---

編集後に編集

広告。enum将来、値を変更または並べ替えたときにも機能するように、値のインデックスを作成するというあなたの意見。これを行う簡単な方法がありますenums!これは基本的にビットフィールドとの中間でありenums、型安全性とすべてのenum機能を保持し、ビットフィールドの利点を備えています。

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何も削除する必要はありません;)。

于 2013-01-11T16:42:42.140 に答える
3

間違いを犯したと思う点がいくつかあります

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

これにより、の権限を設定するときに、他のグループのすべての権限がクリアされgroupます。他のグループの権限を保持する場合は、

// clear permissions for `group`
this.permissions &= ~(0x7 << group * PERMISSIONS);
// set permissions for `group`
this.permissions |= permissions << group * PERMISSIONS;

これにより、他のグループの権限は変更されません。

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

これにより、すべてのグループの権限と、追加する権限が混在しますgroup。私はあなたがそこに欲しいものがあると思います

this.permissions |= permission << group * PERMISSIONS;

何も変更せずに追加するだけpermissionです。group

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

ここで間違った方向にシフトしています。もしgroup > 0、ビット単位で、最下位3つのビットの中にビットがない場合、

return ((permissions >> group *PERMISSIONS) & permission) == permission;
于 2013-01-11T17:19:11.830 に答える