位运算使用场景之标志位(打标)

这是某租房 APP 的截图,对房源的描述除了:房源标题,价格,地理位置等基本信息之外,还有如上图的配套服务属性:是否有电视、是否有冰箱、是否有洗衣机等。

对于基本信息,可以定义表字段来一一对应,但是对于属性信息则很难用表字段来描述。首先,这类字段太多,可能动则几十个,其次这类字段可能经常会变,可能哪天又增加一个“是否地铁物业”。

其实分析发现,这类字段只会有两种情况:是和否。要知道计算机里的位也只有是和否两种情况。那么可以将这些属性字段统一放到一个属性位字段里,通过位打标的方式存储。

表设计如下,关注 prop_mask 字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- PostgreSQL 数据库
CREATE TABLE house_resources
(
house_id character varying(32) NOT NULL, -- 房源 ID(主键)
house_no character varying(20), -- 房源编码(业务流水号)
house_type character varying(2), -- 房源类型
house_status character varying(2), -- 房源状态
house_title character varying(100), -- 房源标题
community_name character varying(50), -- 小区名称
rent_price integer, -- 租金
...
prop_mask integer, -- 房源属性
created_by character varying(32), -- 创建人
created_date timestamp without time zone DEFAULT now(), -- 创建时间
updated_by character varying(32), -- 修改人
updated_date timestamp without time zone DEFAULT now() -- 修改时间
);

在后台代码中定义每位的含义:

1
2
3
4
5
6
7
public static class HousePropMask {
public static final int TV = 0X0001; // 电视
public static final int WASHING_MACHINE = 0X0002; // 洗衣机
public static final int AIR_CONDITIONING = 0X0004; // 空调
public static final int WATER_HEATER = 0X0008; // 热水器
public static final int SOFA = 0X0010; // 沙发
}

这样如果用 Integer 类型存储 PropMask 的话,可以存储 4 * 8 -1 = 31 位,如果还不够的话可以使用 Long 类型。扩展起来很方便。

操作:

添加属性,比如:设置房源有电视,则:house.setPropMask(house.getPropMask() | HousePropMask.TV)

移除属性,比如:移除之前有的电视属性,则:house.setPropMask((house.getPropMask() | HousePropMask.TV) ^ HousePropMask.TV)

判断属性:比如:判断房源是否有电视,则:

1
2
3
4
5
if ((house.getPropMask() & HousePropMask.TV) == HousePropMask.TV) {
// 有
} else {
// 无
}

这类思想的应用场景还很多,比如:电商系统里的商品属性,订单属性。