You knew that such an answer should have appeared sooner or later, so here it is:
Although using bitmasks is arguably the fastest and has the lowest amount of memory for all alternatives, it is also very error prone and this is generally not recommended, except in some cases with a very edge. This is a classic low level tool. If everything is done correctly, it works wonders; if they are used incorrectly, it can cause damage.
Therefore, the right approach is to use higher-level abstractions for this, namely enums and EnumSets . Of course, speed and memory are the same, although, of course, a little worse. In general, they are absolutely sufficient. There are many ways to do this based on your exact context and needs. One possibility might be:
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; } }
This in itself allows you to do exactly what you did, but with two differences:
- Now it's safe and safer, I think. No need to reinvent the wheel.
- It uses more memory. Not much, because
EnumSet is small, usually just long .
EDIT to respond to OP comment about storing EnumSet in the database:
Yes, this can be a problem, since saving int infinitely simpler. If you still think that you are sticking to using EnumSet , then I have several options:
Look at SO. People tried to solve the problem before.
Save the value names in EnumSet :
Permissions p = new Permissions(); p.alliance().addAll(EnumSet.of(Permission.BREAK, Permission.BUILD)); for (Permission permission : p.alliance()) { System.out.println(permission); }
Then you can easily restore the values:
for (String word : stringsFromDtb) { p.alliance.add(Permission.valueOf(word)); }
Save the ordinals. This is VERY dangerous since you can easily break it with a change in the Permission enumeration. In addition, any random number can be cast to break it.
Permissions p = new Permissions(); p.alliance().addAll(EnumSet.of(Permission.BREAK, Permission.BUILD)); for (Permission permission : p.alliance()) { System.out.println(permission.ordinal()); }
Then you can easily restore the values:
for (int ordinal : ordinalsFromDtb) { p.alliance.add(Permission.values()[ordinal]); }
Serialize EnumSet usual way and store binary data directly or BASE64ed. Umm.
---
EDIT after EDIT :
Ad. Your comments on creating indexes for your enum values ββso that if you change or change them in the future, it will still work. There is an easy way to do this with enums ! This is basically the middle path between enums and enums , it retains type safety and all the functions of enum and still has the advantages of bitfields.
public enum Permission { BUILD (0b0001), BREAK (0b0010), INTERACT(0b0100); private final int index; private Permission(int index) { this.index = index; } public int index() { return index; } }
Then you save the indexes in your database and just make sure that the parsing of them is right. In addition, in the future, it only helps to comment (not delete) any unnecessary enumeration values ββso that they still remain visible to you, and you will not use this index. Or just mark it as @Deprecated and you don't need to delete anything;).