使用位运算实现网页中的过滤、筛选功能实例
作者:junjie 发布时间:2023-06-04 10:00:31
最近 * 丝的公司想要为以前的那个网页产品加上一个过滤的功能,废话不多说,直接看筛选的界面是啥样的吧:
可以看出,我们的Message分为Critical、Error等6种类型。现在需要进行过滤,用户可以选择查看其中的一项或多项。
这种需求是很常见的,在大多数情况下,如果可选项是不固定的(比如对于学生信息管理系统,按照班级进行筛选),那么就可能需要借助复杂的SQL语句。例如我们可以写成如下这种形式:
SELECT * FROM [Student] WHERE [ClassId] = 1 OR [ClassId] = 2 OR...
这样就要对SQL语句进行拼接,根据用户选择了哪些内容来动态生成SQL语句。
假如不是数据库操作,而是内存中一片数据的判断,假如用户选择的内容存放在Selected[]数组中,那么对于需要判断的每一项,我们都需要进行一连串的判断操作,需要写一个双重循环。不仅写起来比较复杂,如果项目过多在效率上也令人堪忧。
如果我们需求是像上图那样,Type是固定的并且也不会很多,那么有没有一种比较简单的方式呢?
从标题可以看出,可以使用位运算。在各类高级语言的源代码中大量地运用了这种方式。对于可枚举类型,取值不是顺序的1、2、3、4,而是1、2、4、8按照2的倍数增长,这就是使用二进制位进行判断。
现在我用我刚做的东西来简单叙述一下如何实现吧。当然实现这个很简单,在这里记录更多地是为了增加博文的数量^_^。
在.NET中,一个int类型占4个字节,也就是32位,除去符号位(当然也可以使用无符号整数,这里为了简单期间都使用int),每一位都可以标识一个消息的类型。为此,我们定义枚举类型如下:
public enum CategoryType: int
{
Unknown = 1,
Critical = 2,
Error = 4,
Warning = 8,
Information = 16,
Verbose = 32,
Other = 1024
}
如果用二进制表示,这些消息分别是0000 0001、0000 0010、0000 0100、0000 1000、00010000、…… 假如用户选择了Unknown、Error和Waring三种类型,把这三个数字相加,就是1+4+8=13,二进制表示就是0000 1101。为1的位正好对应了用户选择的类型,为0的位正好对用用户没选的类型。
在客户端,使用Javascript就可以使用下面这种简单的方式计算用户选择的值:
var category = 0;
if (filterVM.ckType2Checked()) category |= 2;
if (filterVM.ckType4Checked()) category |= 4;
if (filterVM.ckType8Checked()) category |= 8;
if (filterVM.ckType16Checked()) category |= 16;
if (filterVM.ckType32Checked()) category |= 32;
if (filterVM.ckType1024Checked()) category |= 1024;
当然,由于这种特殊的设计,使用加法运算的效果是完全相同的。
这样把用户选择的内容传给服务器就是一个简单的整数,比如上面的13表示用户选择的三种类型。而且在数学上可以证明,对于每一个整数,对应的枚举类型的组合是唯一的(要证明看似很难,但如果使用二进制进行思考便会显而易见)。
在服务器端对每种消息进行判断时,只需要使用与运算,便能知道消息是否在用户选择的范围内(只要结果不等于0,说明某个枚举类型对应的那一位在相加的结果中。如果使用符号位,那么可能会出现负数)。
举两个简单的例子吧,假如客户端传过来的是13(0000 1101),某一种消息是Waring=8(0000 1000),相与的结果为8(0000 1000)与消息的类型相同(只需判断是否不等于0即可)。对于消息为16(0001 0000)的情况,相与的结果肯定是0。代码如下:
for (int i = 0; i < list.Count; i++)
{
if (((int)list[i].Category & categoryFilter) > 0 && ((int)list[i].Direction & directionFilter) > 0)
yield return list[i];
}
对于要选出所有的类型的情况,只需要把上述categoryFilter设置为全部为1的数字即可(0x7fffffff,如果使用符号位就是0xffffffff)。
在客户段根据这个相加后的结果进行控件绑定的使用,要判断某个复选框是否应该选中时,按照相同的逻辑相与即可:
this.ckType2Checked((category & 2) > 0);
this.ckType4Checked((category & 4) > 0);
this.ckType8Checked((category & 8) > 0);
this.ckType16Checked((category & 16) > 0);
this.ckType32Checked((category & 32) > 0);
this.ckType1024Checked((category & 1024) > 0);
需要注意的是,这个方法不是万能的。如果可选项是不固定的,使用位运算有可能反而会很麻烦,因为我们要编写程序存储每一种选项对应的数字。
如果可选项是固定的但是数量很多(比如几千种),那么我们可以用一串整数进行表示,也会很方便(当然显示也不会有这样变态的筛选功能,即使有用户也不大可能在几千种里面去选,如果出现这种情况,只能说设计有问题)。
这种做法有一个比较大的弊端,就是改动筛选内容时服务器和客户端都会改。不过还好改动不会太大,只需要改变服务器的enum和客户端的控件即可……
当然这种思想也是今后设计程序时的一种很好参考。


猜你喜欢
- 本文实例讲述了C#获取web.config配置文件内容的方法。分享给大家供大家参考。具体实现方法如下:1.ConfigurationMana
- 本文主要介绍了C# 泛型字典 Dictionary的使用详解,分享给大家,具体如下:泛型最常见的用途是泛型集合,命名空间System.Col
- 本文实例为大家分享了struts2实现多文件上传的具体代码,供大家参考,具体内容如下首先搭建好struts2的开发环境,导入struts2需
- using System;using System.Collections.Generic;using System.ComponentMo
- jmap命令可以打印java进程的JVM堆信息,今天在某台机器上运行该命令查看 19560进程的堆信息jmap -heap 19560出现以
- 前言在我们公司里,不同的服务之间通过Feign进行远程调用,但是,我们在尝试使调用可重试时遇到了一个小问题,Feign框架本身可以配置的自己
- java怎么判断三位数先定义个测试数字,如图然后可以把数字转换成字符串来判断它的长度是否为3,如图获取判断数字范围是否在100到1000之间
- 本文实例为大家分享了PropertyDescriptor反射调用set和get方法,供大家参考,具体内容如下第一段:package com.
- 1.由json字符串转换成Map对象如json字符串:{"contend":[{"bid":&quo
- 此组件解决的问题是:「谁」在「什么时间」对「什么」做了「什么事」本组件目前针对 Spring-boot 做了 Autoconfig,如果是
- 简单之美,springmvc,mybatis就是一个很好的简单集成方案,能够满足一般的项目需求。闲暇时间把项目配置文件共享出来,供大家参看:
- 欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demo
- 我们都知道java是一种面向对象的编程语言,但是在实际意义上java并不是纯面向对象,因为面向对象的意义就是万物皆对象,那么如果说int类型
- 引言依照领导要求区分了两种压测模式:固定次数压测和固定时间压测。此前一直沿用的都是固定次数,所以本次第二版剥离了固定次数的模式增加了固定时间
- 这篇文章主要介绍了Java线程并发访问代码分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参
- 引言翻译自:towardsdev.com/mastering-e…Kotlin 作为现代的、强大的编程语言,可以给开发
- 本文实例为大家分享了Android实现页面短信验证功能的具体代码,供大家参考,具体内容如下目标效果:上一篇博文介绍的是使用SDK中自带的验证
- 前言springmvc中有两种很普遍的AOP实现:1.过滤器(Filter)2. * (Interceptor)本篇面对的是一些刚接触spr
- 先看效果:输入内容,点击生成二维码:点击logo图案:代码:QRCodeUtil:package com.example.administr
- Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。自从5.0开始,jdk在java.util.concurrent