巧用位运算来实现状态处理逻辑
Contents
我们写程序的时候, 常常会遇到状态标志位的处理.
例如, 在一个系统中, 我们要发各种类型的通知, 而这种通知是用户可以设置是否接收的; 又或者是各种权限处理逻辑.
质数相乘
最近在到公司一位同事写了一段代码, 它是用来处理用户是否设置接收某些类型的系统通知的. 他的方案是:
每一种通知类型, 都是一位质数(只能被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