编程技巧:使用整数同时进行多个true|false判断(修改版)

Scene 情景假设

  1. 情景一:银行账户的快速判断;
    • 假设需要判断某银行用户的其中一个账号(profileA),币种(Currency)为人民币(CNY),余额是否大于1,0000,然后进行某业务逻辑处理。
  2. 情景二:验证文件的扩展名是否合格。

Abstract 概述

为了进行场景1的判断,需要判断/验证该账号是否为profileA、币种是否为CNY,余额是否大于1,0000这3种情况,在代码中可能会写出一大堆if...else语句,不仅不雅观,还不便于理解。如果使用一个整数int来同时标识多个判断的结果,就可以避免一大堆if...else的情况。当然这个例子只是作为demo说明情况,具体怎么处理还得根据实际出发,而且要是需要作更多判断,这个方法就会更实用。


Description 详细说明

原理分析

对于计算机来说,所有数据都是由二进制数来表示,对于每一位(bit)来说,有01这2种情况,分别对应的boolean值为false和true。在Java中,每个int的大小为4 byte(注意是基本类型的int,而不是引用类型的Integer),一共有4*8=32位(bit),每一个位可对应一个boolean值的话,那么1个int最多可以同时对应32个boolean值。

具体步骤

大概可分为3步:

  1. 定义每个二进制位数上的含义(即标准),并在代码中以十六进制的形式表示;(其实十进制也可以,只是在十六进制下其实就是1/2/4/8这几个数字在不同的位上循环,比十进制数要直观,可以在下图感受一下)
  2. 将多个待验证的条件通过逻辑与(OR, "|")组合起来(token);
  3. 根据第一点的标准,计算出当前值(code);
  4. 若要判断满足token的所有条件,将当前值和token进行逻辑与(AND, "&")运算,所得结果再与token比较;
    • return (mod & modifier) == modifier;
  5. 若要判断满足token的至少一个条件,将当前值和token进行逻辑与(AND, "&")运算,所得结果再与0比较;
    • return (mod & modifier) != 0;

JDK中的实现:java.lang.reflect.Modifier

  1. java.lang.reflect.Modifier中定义了变量的修饰符(modifier,即staic/final/public/private等);
  2. 在Modifier类中并没有进行多条件的组合,但我们可以在自定义的类中进行组合;
    • public static final int PUBLIC_STATIC_FINAL = PUBLIC | STATIC | FINAL;
  3. 将要验证的编码(整数)传入,与modifier进行逻辑与(AND, "&")运算,再根据具体情况判断;
    • return (mod & PUBLIC) != 0;
    • return (mod & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL;

代码中:

public class Modifier {
	public static final int PUBLIC           = 0x00000001;
	public static final int PRIVATE          = 0x00000002;
	public static final int PROTECTED        = 0x00000004;
	public static final int STATIC           = 0x00000008;
	public static final int FINAL            = 0x00000010;
	public static final int SYNCHRONIZED     = 0x00000020;
	public static final int VOLATILE         = 0x00000040;
	public static final int TRANSIENT        = 0x00000080;
}

转换成表格:

java.lang.reflect.Modifier二进制十进制十六进制
public0000,00012^0= 10x00000001
private0000,00102^1=20x00000002
protected0000,01002^2=40x00000004
static0000,10002^3=80x00000008
final0001,00002^4=160x00000010
synchronized0010,00002^5=320x00000020
volatile0100,00002^6=640x00000040
transient1000,00002^7=1280x00000080

验证:主要看System.out.println();打印输出的几行即可,其他都是辅助理解。

package com.scv.lawrence.multicompare;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import static com.scv.lawrence.multicompare.DemoTools.STATIC_FINAL;
import static com.scv.lawrence.multicompare.DemoTools.compare;
//为了减少篇幅,实际开发中个人不推荐使用静态导入

public class FirstStep {

	/* 定义了3个不同修饰符组合的变量(static、final),并在代码中验证 */
	private static int age = 999; //age → static
	private static final String name = "Lawrence"; // name → static + final
	public final String gender = "Male"; //gender → final

	public static void main(String[] args) throws Exception {
		Field myAge = FirstStep.class.getDeclaredField("age");
		int ageMod = myAge.getModifiers();
		System.out.println("age的modifier(十进制): " + ageMod);
		System.out.println("\t" + "是否包含static和final:" + compare(ageMod, STATIC_FINAL));
		System.out.println("\t" + "是否包含static:" + compare(ageMod, Modifier.STATIC));
		
		Field myName = FirstStep.class.getDeclaredField("name");
		int nameMod = myName.getModifiers();
		System.out.println("************");
		System.out.println("name的modifier(十进制): " + nameMod);
		System.out.println("\t" + "是否包含static和final:" + compare(nameMod, STATIC_FINAL));
		System.out.println("\t" + "是否包含static:" + compare(nameMod, Modifier.STATIC));
		
		Field myGender = FirstStep.class.getDeclaredField("gender");
		int genderMod = myGender.getModifiers();
		System.out.println("************");
		System.out.println("gender的modifier(十进制): " + genderMod);
		System.out.println("\t" + "是否包含static和final:" + compare(genderMod, STATIC_FINAL));
		System.out.println("\t" + "是否包含static:" + compare(genderMod, Modifier.STATIC));
	}
		
}

/* output:
 * 
 * age的modifier(十进制): 10
 * 是否包含static和final:false
 * 是否包含static:true
 * ************
 * name的modifier(十进制): 26
 * 是否包含static和final:true
 * 是否包含static:true
 * ************
 * gender的modifier(十进制): 17
 * 是否包含static和final:false
 * 是否包含static:false
 */

Example 场景应用

场景一 银行账户

  1. 自定义Profile类,在实例化的时候就计算并保存其类型;
  2. 在ToolBox类中定义各种标识的含义;
  3. 在main()中对profile进行验证并执行相应业务逻辑;
package com.scv.lawrence.multicompare;

public class ThirdStep {
	public static void main(String[] args) {
		Profile[] profiles = {new Profile("ABC0001", "CNY", 12_0000),
						new Profile("SRC0001", "CNY", 21_0000),	//target profile
						new Profile("ZZZ666GRE", "USD", 9000)};
		
		for(Profile p: profiles){
			if(ToolBox.isTargetProfile(p, ToolBox.PRO_A_CNY_GT)){
				//Do something.
				System.out.println("Got target profile.");
			};
		}
		
	}
}


class ToolBox{
	public static final int PROFILE_A = 0x0000_0001;	//0001
	public static final int CNY = 0x0000_0002;	//0010
	public static final int GT_10K = 0x0000_0004;	//0100
	public static final int LT_10K = 0x0000_0008;	//1000
	
	public static final int PRO_A_CNY_GT = PROFILE_A | CNY | GT_10K;	//0111,profileA、币种为人民币、余额大于1,0000
	public static final int PRO_A_CNY_LT = PROFILE_A | CNY | LT_10K;	//1111,profileA、币种为人民币、余额不足1,0000
	
	protected static void setToken(Profile p){
		if("SRC0001".equals(p.getName())) p.token += 0x0000_0001;
		if("CNY".equals(p.getCurrency())) p.token += 0x0000_0002;
		if(p.getBalance() > 1_0000) p.token += 0x0000_0004;
	}
	
	public static boolean isTargetProfile(Profile p, int mod){
		return (p.getToken() & mod) == mod;
	}
}

class Profile{
	private String name;
	private String currency;
	private double balance;
	protected int token;
	
	public int getToken(){
		return this.token;
	}
	
	public Profile() {}
	public Profile(String name, String currency, double balance){
		this.name = name;
		this.currency = currency;
		this.balance = balance;
		ToolBox.setToken(this);
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCurrency() {
		return currency;
	}
	public void setCurrency(String currency) {
		this.currency = currency;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	};
	
	
}

如需扩展,在ToolBox定义新的内容即可添加新的业务即可。
p.s:数字下划线是JDK 1.7的语法糖,仅有方便(人类)阅读的作用,编译时会自动去掉。如1_0000_0000、100_000_000、100000000这几个数字在编译后都一样,但极大地方便了阅读。

场景二 验证多个文件扩展名

  1. 值得注意的是,由于是判断是否满足一个或多个条件,因此将判断表达式改为return (code & RULER) != 0;
package com.scv.lawrence.multicompare;

public class SecondStep {

	private static String[] fileNames = {"picA.jpg", "picB.jpg", "picC.png", "picD.gif", "picE.jpeg", "picF.abc"};
	
	public static void main(String[] args) {
		checkSuffix(fileNames);
	}
	
	public static boolean checkSuffix(String[] fileNames){
		boolean flag = true;
		for(String s : fileNames){
			int c = s.lastIndexOf(".");
			if(c != -1){
				String suffix = s.substring(c + 1).toLowerCase();
				int code = getCode(suffix); //获取文件扩展名并判断
				flag = Ruler.isAllow(code);
				System.out.println(s + ": " + (flag ? "pass" : "not pass"));
				
				if(!flag) return false; //只要有一个不合格的格式就直接返回false
			}else{
				return false; //没有扩展名,直接返回false
			}
		}
		return flag;
	}
	
	public static int getCode(String suf){
		int code = 0;
		switch(suf){
			case "jpg": //switch语句使用字符串作判断条件也是jdk1.7的特性,其实质仍然是int(编译后计算hashCode)
				code = Ruler.JPG;
				break;
			case "jpeg":
				code = Ruler.JPEG;
				break;
			case "png":
				code = Ruler.PNG;
				break;
			case "gif":
				code = Ruler.GIF;
				break;
			default:
				code = Ruler.DEFAULT;
				break;
		}
		return code;
	}
	
}

class Ruler{
	public static final int DEFAULT = 0x0;
	public static final int JPG = 0x1;
	public static final int JPEG = 0x2;
	public static final int PNG = 0x4;
	public static final int GIF = 0x8;
	
	public static final int RULER = JPG | JPEG | PNG | GIF;
	//只允许jpg、jpeg、png、gif格式的文件
	
	public static boolean isAllow(int code){
		return (code & RULER) != 0;
	}
}

Summary 总结

  • 优点
    1. 避免大量的if...else判断;
    2. 易于扩展维护代码;
    3. 二进制的计算,高效;
  • 缺点
    1. 需要先定义作比较的条件和计算出具体值,占了一定的代码量;
    2. 初次接触的话可能不易理解;
  • 与switch的比较
    • 轻松进行多个条件的判断,且可以进行子集条件的判断,即对于满足A|B|C|D的条件,也会同时满足A|B、B|D或B|C|D等等。switch并不具备这样的能力。

Reference 参考

转载于:https://my.oschina.net/SCVTheDefect/blog/526757

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值