利用|,&,^,~,<<,>>>写出高效艺术的代码

简介:

大家在阅读源码的时候经常会看到一些比如下面这样特别难理解的代码。

cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
            (motionEvent.getActionIndex()
                    << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
order = ((order) >> (INDEX_OFFSET -1) + 1) << INDEX_OFFSET;

类似与这种“高大上”的代码在各类大神写的代码或源码中随处可见。

这种代码是什么意思?为什么要这么写?这么写的好处?怎么才能写出这样的代码?我们就带着这些疑问来看下面的文章吧。

逻辑运算符:

逻辑运算符主要用来计算真假值(boolean),通常用在条件判断语句中。

逻辑运算是我们平时用的最多的,主要有&&,||,&,|。相信大家都熟悉,所以这里只简单介绍一下&&和&的区别:

对于&&和||,如果左边判断通过就不会判断右边了。而&和|则是两边等式都需要计算。

(所以这里有个技巧,如果不需要两边都判断的话则用&&或||并且可以把需要用时多的计算条件放在右边,用时少的放在左边)

位运算符:

位运算符主要对二进制数(int)做操作。

包括:与(&),或(|),非(~),异或(^),左移(<<),右移(>>>),正负右移(>>)。

与(&):两边都是1(true),结果才为1(true),否则结果为0(false)

例:1 & 2 = 0001 & 0010 = 0000 = 0

或(|):两边有一个为1(true),结果就为1(true)。两边都是0(false),结果才是0(false)

例:1 |
3 = 0001 | 0011 = 0011 = 3

非(~):如果位为0(false),结果是1(true),如果位为1(true),结果是0(false)

例:~ 4
= ~0100 = 1011 = 11

异或(^):两个操作数的位中,相同则为0(false),不同则为1(true)

例:1 ^ 4 = 0001
^ 0100 = 0101 = 5

左移(<<):向左移动运算符右边指定的位数,并且在低位补零。或相当于将(左边的值)乘上2
的(右边值) 次方

例:1
<< 4 = 0001向左移动4位 = 10000 = 16 或者 = 1 * 2^4 = 16

右移(>>>):向右移动运算符右边指定的位数,并且在高位补零。或相当于将(左边的值)除以2
的(右边值) 次方

例:4
>>> 2 = 0100向右移动2位 = 0001 = 1 或者 = 4 / 2^2 = 1

正负右移
(>>)
:向右移动运算符右边指定的位数。如果是正数,在高位补零,如果是负数,则在高位补1

例:

4 >> 2 = 0100向右移动2位后高位补0 = 0001 = 1

-4 >> 2 = -0100向右移动2位后高位补1  = 1011 向右移动两位后高位补1 = 1110 = 14

在程序中使用位运算

首先我们要有一个概念,使用二进制的目的就是要用1和0当做标志位,通常把1当成true,0当成false。这样就可以在一个变量中携带多个标志位信息。

这里举个例子,大家看后就明白了。

需求是这样的,有4个权限判断,分别是A,B,C,D。我们要根据不同的权限组合来进行不同的操作。

	public static final int PASS_BY_A = 1;	//0001
	public static final int PASS_BY_B = 2;	//0010
	public static final int PASS_BY_C = 4;	//0100
	public static final int PASS_BY_D = 8;	//1000

	public static final int PASS_BY_ABCD = PASS_BY_A|PASS_BY_B|PASS_BY_C|PASS_BY_D;  //1111

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		boolean isA = false;
		boolean isB = false;
		boolean isC = false;
		boolean isD = false;
		int isPass = -1;

		// 获取权限然后为各个权限变量赋值,这里忽略
		initParameter();

		// 使用传统的boolean去做判断,是否满足ABCD四个权限
		if (isA & isB & isC & isD) {

		}

		// 使用二进制做判断,是否满足ABCD四个权限
		if ((isPass & PASS_BY_ABCD) == 0) {

		}
	}

通过对比可以看出,使用二进制做判断,变量的使用量,效率还有代码简洁程度大大提高。这也是在各种各样的源码里二进制和位运算被经常使用的原因。

位运算使用技巧总结:

最后我总结出了一些我在使用二进制和位运算时发现的一些小技巧。

a | b:合并ab中的1。

例1:a为1100(12),b为0001(1),a|b=1101(13)。

例2:a=0001(1)。这时候想让a=1111(15)。最简单的做法就是找个b=1110(14)。a | = b (a = a | b)

a & b:从a中去掉b中的1。

例1:a为1010(10),b为1000(8); a & b = 0010 (2)。如果 a & b = 0000(0),这就说明a和b的标志位完全相同,如果 a & b != 0 表示ab不同。经常使用它来做判断

例2:a=1111(15)。这时候想让a=1001(9)。最简单的做法就是找个b=0110(6)。a & = b (a = a & b)

a ^ b:找出ab中的不同/相同(1为不同,0为相同)。

例1:比如a为1100(12),b为0111(7)。a ^ b = 1011(11)

a << b 和 a >>> b:左移和右移通常都是为二进制赋值,和改变值

例1:a=0001(1)。a<<1=0010(2), a<<2=0100(4), a<<3=1000(8),配合for循环可以很快的为二进制赋值。

例2:a=1100(12), INDEX_OFFSET = 0001(1)。 a >>>= INDEX_OFFSET = 0110(6), a >>>= INDEX_OFFSET = 0011(3)。

而且大家在程序中做2倍数乘除法的时候最好是使用左移和右移操作,这样效率会提高很多。

最后列出4*4的二级制数集合

0=0000 1=0001 2=0010 3=0011
4=0100 5=0101 6=0110 7=0111
8=1000 9=1001 10=1010 11=1011
12=1100 13=1101 14=1110 15=1111

标注颜色的2组为比较常用的。可以的话大家可以背下来几组,免得现用现算,这样编程效率就会大大增加。

一般在做程序的时候4*4个标志位基本够用了。如果不够可以5*5,6*6等等。使用标志位判断越多的时候二进制的优势就越明显。

总结&感受:

我在平时做项目,做程序的时候很少有机会使用算法。我觉得使用二进制和位运算就可以算做为一种简单的算法,因为使用算法的目的就是为了给程序“减压”,但是相应会给程序员“增压”。为了不做Code Monkey或者Code Farmer,我们应该主动的找机会给自己“增压”。在平时有机会一定要尽可能多的使用算法,多学习源码和大神的编程风格,提高自己的水平,写出高效、优雅、艺术的代码!

相信大家看完这篇文章后,会对二进制在程序中做标志位的用法有所了解,最起码在看源码的时候不会一头雾水。另外我目前只发现了二进制在程序中做标志位的作用,也许还有其他的应用,大家知道的话希望可以分享一下。

利用|,&,^,~,<<,>>>写出高效艺术的代码

时间: 2024-10-27 05:43:41

利用|,&,^,~,<<,>>>写出高效艺术的代码的相关文章

利用|,&amp;amp;,^,~,&amp;lt;&amp;lt;,&amp;gt;&amp;gt;&amp;gt;写出高效艺术的代码

简单介绍: 大家在阅读源代码的时候常常会看到一些比方以下这样特别难理解的代码. cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); order = ((order) >> (INDEX_OFFSET -1) + 1) << INDEX_OFFSET; 类似与这样的"

写出稳定的Modbus代码之点滴经验

1.引言 Modbus是工业领域重要的协议,物理层有常见的RS485双绞线和TCP,所以又常说Modbus 485开发和Modbus TCP开发. 前者就是串口通信,比较简单.后者涉及到网络协议,复杂度高出好几个层次. 但是如果有稳定的TCP通信做铺垫,这两种Modbus的区别就不大了,都是数据包的解析而已,能共用大部分代码. 本文不讨论Modbus协议如何读写一个Register或Coil之类的,这些东西看看文档或者网上搜下博客教程就知道了. 本文目标是讨论如何写一个稳定的Modbus通信驱动

如何编写高效的Android代码

时间是很宝贵的东西,在编写Android代码的时候尽可能的编写出高效的Android代码可以节省你很多的时间,让你有时间去泡妞.去陪女朋友.去陪基友,去吃饭.去娱乐.去睡觉!毕竟,谁都不想整夜整夜的加班,然后还乱吼:时间都去哪了?!下面就整理了怎样提高效率,编写高效的Android代码!看到这,偷笑了吧,表掩饰,我已看到! 对于如何判断一个系统的不合理,这里有两个基本的原则: 一.不要做不必要做的事情. 二.尽可能的节省内存的使用优化链接: http://c.tieba.baidu.com/p/

掌握解决问题的艺术,学会迭代开发,成为协作开发的专家,然后为写出更好的代码而担忧(转)

很多开发人员普遍犯有一个错误,认为他们的工作就是写代码.这不是一个开发人员要做的事情. 一个开发人员的工作是解决问题. 解决问题的一部分过程通常涉及到写代码,但是这部分工作是非常非常小的.开发有用的东西才花更多时间. 明白如何迭代开发,随着对问题有更好的理解,你才能给难题增加一些小功能,因为从头开发完美的产品是不可能的.不用写代码就能验证功能,因为很明显,写代码是相当昂贵的. 用于测试.评测和抛弃想法的系统也是极其重要的,因为要是没有它,整个开发组将耗费越来越多的精力,还有用来帮助他们执行得更有

【整洁之道】如何写出更整洁的代码(上)

如何写出更整洁的代码 代码整洁之道不是银弹,不会立竿见影的带来收益. 没有任何犀利的武功招式,只有一些我个人异常推崇的代码整洁之道的内功心法.它不会直接有效的提高你写代码的能力与速度,但是对于程序员的整个职业生涯必然会带来意想不到的好处. 如果你还是一个在校学生,或者是刚工作没多久的"菜鸟",那么很有必要接触一些这方面的知识的.很显然,它会帮助你更快的适应企业级开发的要求. 1. 为什么需要代码更整洁? 在考虑代码整洁的时候,我们需要明确的一个前提是,这里不讨论代码的对错. 关于什么是

写可測试的代码

写可測试的代码 不论什么一个软件都是能够測试.在某种意义上,用户的使用过程也就是一个软件測试的过程.但是这并非我们今天要讲的可測试性.我们讲的可測试性指的是代码的可測试性,通俗点儿说就是是一串代码里包括的逻辑是不是能够被单元測试所覆盖.在这篇文章里我会从单元測试的基本概念開始引伸到怎样写单元測试,怎样写可单元測试的代码.文章里全部的样例都是C#写的,一来它是我职业生涯的主力语言.二来C#广为人知,相信对广大职业的或是业余的程序猿来说读懂C#的代码不会是什么特别困难的事情.实际上我描写叙述的方法和

高效重构 C++ 代码

引言 Martin Fowler的<重构:改善既有代码的设计>一书从2003年问世至今已有十几年时间了,按照计算机领域日新月异的变化速度,重构已经算是一门陈旧的技术了.但是陈旧并不代表不重要,恰恰随着演进式设计被越来越广泛的使用,重构技术已经被认为是现代软件开发中的一项必备的基本技能!所以今天在任何软件开发团队中,你都会不时听到或看到和重构相关的代码活动.然而对于这样一种被认为应该是如同“软件开发中的空气和水”一样的技术,在现实中却比比皆见对重构的错误理解和应用.首先是不知道重构使用的正确场合

A1128 | 逻辑想象能力、简洁高效美观的代码、memset的使用情景

写了三遍才AC,这真是对智商极大的侮辱 C++代码: #include <stdio.h> #include <memory.h> #include <math.h> #include <string> #include <vector> #include <set> #include <stack> #include <queue> #include <algorithm> #include &l

如何写出规范的JavaScript代码

作为一名开发人员(WEB前端JavaScript开发),不规范的开发不仅使日后代码维护变的困难,同时也不利于团队的合作,通常还会带来代码安全以及执行效率上的问题.本人在开发工作中就曾与不按规范来开发的同事合作过,与他合作就不能用"愉快"来形容了.现在本人撰写此文的目的除了与大家分享一点点经验外,更多的是希望对未来的合作伙伴能够起到一定的借鉴作用.当然,如果我说的有不科学的地方还希望各路前辈多多指教.下面分条目列出各种规范要求,这些要求都是针对同事编码毛病提出来的,好些行业约定的其它规范