这里有两个关键链接https://blog.csdn.net/weixin_42216574/article/details/82885102
https://www.cnblogs.com/wizarderror/p/11270496.html
对比着看,该文章仅仅完成了“按位与”得到内容;
关于进制和位运算的基础知识参考
1.6 二进制及位运算
位运算符有:&(按位与)、|(按位或)、^(按位异或)、~ (按位取反)。
优先级从高到低,依次为~、&、^、|
1. 按位与操作 &
即:两位同时为“1”,结果才为“1”,否则为0例1:0&0=0; 0&1=0; 1&0=0; 1&1=1(``**有0则0**``)
例2:10&9: 0000 1010 & 0000 1001 = 0000 1000 = 8(``**有0则0**``)
负数按补码形式参加按位与运算
(1)清零。
如果想将一个单元清零,即:使其全部二进制位为0;只要与一个各位都为零的数值相与,结果为零。(有0则0)
(2)取一个数中的指定位
方法:找一个数,对应X要取的位,该数的对应位为1,其余位为零,此数与X进行“与运算”可以得到X中的指定位。
例:设X=10101110,
取X的低4位,用 X & 0000 1111 = 0000 1110 即可得到;
还可用来取X的2、4、6位。
sql应用 ( 1 2 4 8 16 32 ….)
sql中有个很有意思的用法;
加入一个加入多个工会:1联盟 2部落 4铁骑 8出门左拐 16.. 32.. 64.. 这个数字很有意思(都是2的n次密,符合二进制数规则)
假如一个人同时加入了三个公会,他的数据库存储这三个数字的和(联盟+部落+铁骑 = 1+2+4 = 7 跟linux的操作权限似的);那么sql中怎么查询呢;
可以用【按位与操作符 &】;
select * from person
where gonghui & 7 -- 这个意思就是工会字段与 7(十进制)做按位与操作,如果结果不等于零就将该条数据查出来;即:查person表中所有加入过联盟、部落、铁骑中任意一个或多个的人;(模糊匹配,容易有bug)
select * from person
where gonghui & 7 = 7 -- 查同时加入了联盟+部落+铁骑的人(精确匹配)
还有一种写法
select * from person
where gonghui & 6 = gonghui --
详细解释:
第一种写法: 所有的玩家都加入了多个工会,我们筛选加入了1联盟+2联盟4铁骑中任意一个或者任意多个工会的玩家;
第二种写法: 在一的基础上我们找同时加入了1+2+4工会的人
第三种写法:我们要找一批人,这批人的特性是他们加入的所有工会必须符合都在一个特定的池子里(即:6=2+4),即:如果一个玩家加入的工会是2 或者是4 或者同时加入了2和4,那么他是符合要求的,但是如果他还如果1工会,那就不符合要求了
二跟三的区别就是等于谁的问题;等于后者就是前者范围大,找到前者包容后者并且精确包含的后者;等于前者,后者的范围一般比较大,如果前者有超出后者的范围的数据,直接淘汰了,并且也是精确匹配与前者相同的数据
<if test="useType != null and useType != ''">
and et.use_type & #{useType}=#{useType}
</if>
select 7 & 1 from dual; -- 1
select 7 & 2 from dual; -- 2
select 7 & 3 from dual; -- 3
select 7 & 4 from dual; -- 4
select 7 & 5 from dual; -- 5
select 7 & 6 from dual; -- 6
select 7 & 7 from dual; -- 7 [ 1 + 2 + 4 ]
select 7 & 9 from dual; -- 1 【1+8】 [1001 & 0001 = 0001 所以是1,这里容易有bug ]
如何将上边的1 2 4 8…值的和拆分出来
public static List<String> getDescArrs (Integer aa) {
Assert.isTrue (Arrays.stream(UseTypeEnum.values()).mapToInt(UseTypeEnum::getValue).sum() >= Objects.requireNonNull(aa), str());
List<String> collect = Arrays.stream(UseTypeEnum.values()).filter(o -> (o.value & aa) != 0).map(UseTypeEnum::getDesc).collect(Collectors.toList());
return collect;
}
public static List<Integer> getValueArrs (Integer aa) {
Assert.isTrue (Arrays.stream(UseTypeEnum.values()).mapToInt(UseTypeEnum::getValue).sum() >= Objects.requireNonNull(aa), str());
List<Integer> collect = Arrays.stream(UseTypeEnum.values()).filter(o -> (o.value & aa) != 0).map(UseTypeEnum::getValue).collect(Collectors.toList());
return collect;
}
public enum UseTypeEnum {
OUTSIDE(1, "外出签约","-WQ-"),
STORE(2, "门店签约","-DZ-"),
CENTER(4, "集中签约","-JZ-") ,
PHONE(8, "手机签约","-SJ-");
private String desc;
private Integer value;
private String prefix;
UseTypeEnum(Integer value, String desc,String prefix) {
this.value = value;
this.desc = desc;
this.prefix = prefix;
}
}
java应用
这里将一个数字转换成多个简单2进制数的数组就用 【第一种】:即:将7改成【1 2 4】数组;如果是将一个字典转换成多个码值的字符串就用【第二种】,即:将7 变成 1 2 4 对应字符串的拼接值得话(总部待审核,总部审核通过,总部驳回)
1. 第一种:
这里的四个方法都是将一个以2的整数次方为基数相加在一起的一个整数进行分拆,分拆成一个个2的整数次方形式
package com.yjyz.erp.trans.srv.common.utils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 二进制数据的处理
*/
@Slf4j
public class BinArray {
@Test
public void test(){
List<Integer> integers = BinArray.splitPower(7);
System.out.println(integers);
List<Integer> integers1 = BinArray.int2BinArray(7);
System.out.println(integers1);
}
/**
* 功能:将一个以2的整数次方为和加在一起的一个整数进行分拆,分拆成一个个2的整数次方形式
*
* @param n 要拆分的数
* @return
* @author zhouxiuyan@yjyz.com
*/
public static List<Long> splitPower(Long n) {
List<Long> list = new ArrayList<Long>();
if (n == null) {
return list;
}
String s = Long.toBinaryString(n);
int len = s.length();
for (int i = 0; i < len; i++) {
Long cur = Long.parseLong(s.substring(len - i - 1, len - i));
if (cur > 0) {
list.add((long) Math.pow(2, i));// 2的i次方
}
}
return list;
}
/**
* 功能:将一个以2的整数次方为和加在一起的一个整数进行分拆,分拆成一个个2的整数次方形式
*
* @param n 要拆分的数
* @return
* @author zhouxiuyan@yjyz.com
*/
public static List<Integer> splitPower(Integer n) {
List<Integer> list = Lists.newArrayList();
if (n == null) {
return list;
}
String s = Integer.toBinaryString(n);
int len = s.length();
for (int i = 0; i < len; i++) {
long cur = Long.parseLong(s.substring(len - i - 1, len - i));
if (cur > 0) {
list.add((int) Math.pow(2, i)); // 2的i次方
}
}
return list;
}
/**
* 转二进制素组
* @param intValue
* @return
*/
public static List<Integer> int2BinArray(Integer intValue) {
if(Objects.isNull(intValue)){
return Lists.newArrayList();
}
try{
int leftMoveCount = 32;
Integer[] max = new Integer[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (intValue == 0) {
return new ArrayList<>(0);
} else {
int a;
int c = 0;
for (int i = 0; i < leftMoveCount; ++i) {
a = intValue & 1 << i; //这里就用到了 【取一个数中 指定位】的作用了
if (a != 0) {
++c;
max[c - 1] = a;
}
}
Integer[] res = new Integer[c];
System.arraycopy(max, 0, res, 0, c);
return Stream.of(res).collect(Collectors.toList());
}
}catch (Exception e){
log.error("int2BinArray报错,intValue:{}, error:{}", intValue, e.getMessage(),e);
}
return Lists.newArrayList();
}
/**
* 转二进制素组
* @param longValue
* @return
*/
public static List<Long> int2BinArray(Long longValue) {
if(Objects.isNull(longValue)){
return Lists.newArrayList();
}
try{
int leftMoveCount = 32;
Long[] max = new Long[]{0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L
, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L};
if (longValue == 0) {
return new ArrayList<>(0);
} else {
long a;
int c = 0;
for (long i = 0; i < leftMoveCount; ++i) {
a = longValue & 1 << i;//这里就用到了 【取一个数中 指定位】的作用了
if (a != 0) {
++c;
max[c - 1] = a;
}
}
Long[] res = new Long[c];
System.arraycopy(max, 0, res, 0, c);
return Stream.of(res).collect(Collectors.toList());
}
}catch (Exception e){
log.error("int2BinArray报错,longValue:{}, error:{}", longValue, e.getMessage(),e);
}
return Lists.newArrayList();
}
}
2. 第二种方式:
枚举的方式;直接将7转化成一个1 2 4 对应的汉字的字符串了;第一种方式只是转换成了【1 2 4】数组;第二种方式更进一步了;
package com.yjyz.erp.trans.srv.contract.enums;
import com.yjyz.core.utils.common.Assert;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 新房垫佣,审核状态枚举
这是有个标准的枚举类;除了 getStrings这个方法有点特殊用法外,别的地方统统可以通用到别的地方;
*/
public enum AdvanceStatusEnum {
ZB_DSH(1, "总部待审核"),
ZB_TG(2, "总部审核通过"),
ZB_BH(4, "总部驳回"),
ZF_DSH(8, "待资方审核"),
ZF_TG(16, "资方审核通过(已认证)"),
ZF_BH(32, "资方驳回");
private Integer value;
private String desc;
/***/
public static AdvanceStatusEnum of(Integer key) {
for (AdvanceStatusEnum item : AdvanceStatusEnum.values()) {
if (item.key.equals(key)) {
return item;
}
}
return null;
}
AdvanceStatusEnum(Integer value) {
this.value = value;
this.desc = "INPUT";
}
AdvanceStatusEnum(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
public static String desc(Integer value) {
for (AdvanceStatusEnum typeEnum : values()) {
if (typeEnum.getValue() == value) {
return typeEnum.getDesc();
}
}
return "unknown";
}
public Integer getValue() {
return value;
}
public String getDesc() {
return desc;
}
public static String str() {
String value = "字段类型限制:";
value += Arrays.stream(AdvanceStatusEnum.values()).map(node -> String.format("[%s,%s]", node.getValue(), node.getDesc())).collect(Collectors.joining(" "));
return value;
}
/**
*这个有个特殊的限定,一定要从1开始,如果从0开始,或者从1之后的任意一个数字都不行;(好像从1之后的数字开始也行哈,有待研究一下)
*/
public static String getStrings (Integer aa) {
// Assert.isTrue (Arrays.stream(AdvanceStatusEnum.values()).filter(o -> (o.value & aa) != 0).mapToInt(AdvanceStatusEnum::getValue).sum() >= Objects.requireNonNull(aa), str()); //这个有点过时了,下边那句更好
Assert.isTrue (Arrays.stream(UseTypeEnum.values()).mapToInt(UseTypeEnum::getValue).sum() >= Objects.requireNonNull(aa), str());
String collect = Arrays.stream(AdvanceStatusEnum.values()).filter(o -> (o.value & aa) != 0).map(AdvanceStatusEnum::getDesc).collect(Collectors.joining(","));
return collect;
}
public static void main(String[] args) {
String strings = AdvanceStatusEnum.getStrings(63);//这个正常
System.out.println(strings);
System.out.println(AdvanceStatusEnum.getStrings(64));//这个报异常;
}
/**
*这个是根据key值返回整枚举实例的;
这个可以很好的配合switch语法;
*/
public static AdvanceStatusEnum of(Integer key) {
for (AdvanceStatusEnum item : AdvanceStatusEnum.values()) {
if (item.key.equals(key)) {
return item;
}
}
return null;
}
}
2. 按位或运算符(|)
0|0=0;0|1=1;1|0=1;1|1=1(有1则1)
即 :参加运算的两个对象只要有一个为1,其值为1。
例如:3|5 即 0000 0011 | 0000 0101 = 0000 0111 因此,3|5的值得7。
另,负数按补码形式参加按位或运算。
“或运算”特殊作用:
常用来对一个数据的某些位 置1。
方法:找到一个数,对应X要置1的位,该数的对应位为1,其余位为零。此数与X相或可使X中的某些位置1。
例:将X=10100000的低4位置1 ,用 X | 0000 1111 = 1010 1111即可得到。
3 异或运算符(^)
00=0;01=1;10=1;11=0;(同0异1)
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
例如:10^-9 即 0000 1010 ^ 1111 0111 = 1111 1101(补码) 原码即为1000 0011 即10^-9 = -3
“异或运算”的特殊作用:
(1)使特定位翻转 找一个数,对应X要翻转的各位,该数的对应位为1,其余位为零,此数与X对应位异或即可。
例:X=10101110,使X低4位翻转,用X ^ 0000 1111 = 1010 0001即可得到。
(2)与0相异或,保留原值 ,X ^ 0000 0000 = 1010 1110。
(3)交换a和b
4 取反运算符(~)
1=0;0=1;
即:对一个二进制数按位取反,即将0变1,1变0。
使一个数的最低位为零,可以表示为:a&~1。
1的值为1111111111111110,再按“与”运算,最低位一定为0。因为“”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
- 位移运算符(<<,>>)
左移运算符(<<)将一个运算对象的各二进制位全部左移若干位
操作数每左移一位,相当于该数乘以2。
右移运算符(>>)将一个数的各二进制位全部右移若干位
操作数每右移一位,相当于该数除以2。