我们写程序的时候, 常常会遇到状态标志位的处理.

例如, 在一个系统中, 我们要发各种类型的通知, 而这种通知是用户可以设置是否接收的; 又或者是各种权限处理逻辑.

质数相乘

最近在到公司一位同事写了一段代码, 它是用来处理用户是否设置接收某些类型的系统通知的. 他的方案是:

每一种通知类型, 都是一位质数(只能被1和它自身整除), 所有系统通知类型的组合就是这些通知类型的乘积.

示例代码:


    @Test
    public void testBitTest() {
        int noticeA = 2;
        int noticeB = 3;
        int noticeC = 5;
        int noticeD = 7;
        int noitceE = 11;

        //假设用户只设置了 A和D
        int userNotice = noticeA * noticeD;

        System.out.println(hasNotice(userNotice, noticeA));//true
        System.out.println(hasNotice(userNotice, noticeB));//false
    }

    private boolean hasNotice(int userNotice, int noticeType) {
        return userNotice !=0 && userNotice % noticeType == 0;
    }

缺点:

int 类型的话, 那最多只能有 9 种状态:

2*3*5*7*11*13*17*19*23

位处理

        int notice_1 = 1 << 0;
        int notice_2 = 1 << 1;
        int notice_3 = 1 << 2;
        int notice_4 = 1 << 3;
        int notice_5 = 1 << 4;

        //假设用户有 1, 2,  3 三种通知类型
        int userNotice = notice_1 | notice_2 | notice_3;
        System.out.println(hasNotice(userNotice, notice_1)); // true
        System.out.println(hasNotice(userNotice, notice_5)); // false


    private boolean hasNotice(int userNotice, int notice) {
        return (userNotice & notice) != 0;
    }

缺点: 这也很明显, 最多只有31种状态.

不过, 位操作的方式在开源项目中比较常见. 比如 Android SDK 中的权限处理, 类Unix系统中的文件权限(rwx)等.

性能对比

bit

package hello;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class BenchmarkTest {
    @Benchmark
    public void testBit() {
        int notice_1 = 1 << 0;
        int notice_2 = 1 << 1;
        int notice_3 = 1 << 2;
        int notice_4 = 1 << 3;
        int notice_5 = 1 << 4;

        //假设用户有 1, 2,  3 三种通知类型
        int userNotice = notice_1 | notice_2 | notice_3;
        hasNotice(userNotice, notice_1); // true
        hasNotice(userNotice, notice_5); // false
    }

    private boolean hasNotice(int userNotice, int notice) {
        return (userNotice & notice) != 0;
    }




    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(BenchmarkTest.class.getSimpleName())
                .forks(1)
                .warmupIterations(5)
                .measurementIterations(5)
                .threads(10)
                .build();
        new Runner(opt).run();
    }
}

结果

Result "testBit":
  5272936.364 ±(99.9%) 1224812.109 ops/ms [Average]
  (min, avg, max) = (4848039.898, 5272936.364, 5673708.549), stdev = 318079.815
  CI (99.9%): [4048124.256, 6497748.473] (assumes normal distribution)


# Run complete. Total time: 00:00:12

Benchmark               Mode  Cnt        Score         Error   Units
BenchmarkTest.testBit  thrpt    5  5272936.364 ± 1224812.109  ops/ms

质数因子

package hello;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class BenchmarkTest {
    //@Benchmark
    public void testBit() {
        int notice_1 = 1 << 0;
        int notice_2 = 1 << 1;
        int notice_3 = 1 << 2;
        int notice_4 = 1 << 3;
        int notice_5 = 1 << 4;

        //假设用户有 1, 2,  3 三种通知类型
        int userNotice = notice_1 | notice_2 | notice_3;
        hasNotice(userNotice, notice_1); // true
        hasNotice(userNotice, notice_5); // false
    }

    private boolean hasNotice(int userNotice, int notice) {
        return (userNotice & notice) != 0;
    }


    @Benchmark
    public void testPrimeNumber() {
        int noticeA = 2;
        int noticeB = 3;
        int noticeC = 5;
        int noticeD = 7;
        int noticeE = 11;

        //假设用户只设置了 A和D
        int userNotice = noticeA * noticeB * noticeC;

        isNotice(userNotice, noticeA);
        isNotice(userNotice, noticeE);
    }

    private boolean isNotice(int userNotice, int noticeType) {
        return userNotice % noticeType == 0;
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(BenchmarkTest.class.getSimpleName())
                .forks(1)
                .warmupIterations(5)
                .measurementIterations(5)
                .threads(10)
                .build();
        new Runner(opt).run();
    }
}

结果

Result "testPrimeNumber":
  4864086.350 ±(99.9%) 3239405.469 ops/ms [Average]
  (min, avg, max) = (3623073.259, 4864086.350, 5503454.074), stdev = 841263.313
  CI (99.9%): [1624680.880, 8103491.819] (assumes normal distribution)


# Run complete. Total time: 00:00:12

Benchmark                       Mode  Cnt        Score         Error   Units
BenchmarkTest.testPrimeNumber  thrpt    5  4864086.350 ± 3239405.469  ops/ms