简单介绍:

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

  1. cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
  2. (motionEvent.getActionIndex()
  3. << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
  1. 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。最高位是补0或是补1 取决于编译系统的规定.

例:

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

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

在程序中使用位运算

首先我们要有一个概念。使用二进制的目的就是要用1和0当做标志位。通常把1当成true,0当成false。

这样就能够在一个变量中携带多个标志位信息。

这里举个样例,大家看后就明确了。

需求是这种,有4个权限推断,各自是A。B,C,D。我们要依据不同的权限组合来进行不同的操作。

  1. public static final int PASS_BY_A = 1; //0001
  2. public static final int PASS_BY_B = 2; //0010
  3. public static final int PASS_BY_C = 4; //0100
  4. public static final int PASS_BY_D = 8; //1000
  5.  
  6. public static final int PASS_BY_ABCD = PASS_BY_A|PASS_BY_B|PASS_BY_C|PASS_BY_D; //1111
  7.  
  8. /**
  9. * @param args
  10. */
  11. public static void main(String[] args) {
  12. boolean isA = false;
  13. boolean isB = false;
  14. boolean isC = false;
  15. boolean isD = false;
  16. int isPass = -1;
  17.  
  18. // 获取权限然后为各个权限变量赋值,这里忽略
  19. initParameter();
  20.  
  21. // 使用传统的boolean去做推断,是否满足ABCD四个权限
  22. if (isA & isB & isC & isD) {
  23.  
  24. }
  25.  
  26. // 使用二进制做推断。是否满足ABCD四个权限
  27. if ((isPass & PASS_BY_ABCD) == 0) {
  28.  
  29. }
  30. }

通过对照能够看出,使用二进制做推断,变量的使用量,效率还有代码简洁程度大大提高。

这也是在各种各样的源代码里二进制和位运算被常常使用的原因。



位运算使用技巧总结:

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

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中的1和b中的1是否全然不同(例1)。合并ab中的0(例2)。

1与不论什么数与运算都得不论什么数(例3)

例1:a为1010,b为0101; a & b = 0000(0)。假设 a & b = 0。这就说明a中的1和b中的1全然不同。

例2:a为1110。b为1001; a & b = 1000(8)。合并ab中的0。或理解为使用0来替换掉原有的位

例3:a为1001000。b为11110000; a & b = 10010000 = a。所以常常有代码中使用在须要的位数标记为1,不须要的位数标记为0,作为遮罩(MASK)。来得到遮罩范围内的值。

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倍数乘除法的时候最好是使用左移和右移操作,这样效率会提高非常多。

将二进制最右面的1的变为0:当前数字减1。然后和原数字做与运算,则能够将当前数字的最右面一个1置为0

例:1100-1=1011, 1100&1011=1000

最后列出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,我们应该主动的找机会给自己“增压”。

相信大家看完这篇文章后,会对二进制在程序中做标志位的使用方法有所了解。最起码在看源代码的时候不会一头雾水。另外我眼下仅仅发现了二进制在程序中做标志位的作用,或许还有其它的应用。大家知道的话希望能够分享一下。

7月4日凌晨:

今天博客发出去半天以后被两个好心人找出了几处错误,并且另一处是十分严重的。我立马从首页把文章撤了下来,晚上回家研究了半天给改过来,并进行了深刻的反思。

确实如同评论里帮我挑错的朋友说的一样,我的理论和实际脱节。我仅仅是简单的使用过两次,看过几段使用二进制的源代码就想当然的以为自己会用了。以为自己从小菜鸟脱离了一步快要成为大神了。通过这次的问题发现自己还是一仅仅小菜鸟,并且心态有问题,有些急躁了。

最開始写博客就是为了对自己学习过程的一个总结。以后能够方便的回想复习。近期開始变成为了完毕一周一篇文章的任务而使自己慢慢脱离了写博客的初衷了。知识和技能还是须要踏踏实实的慢慢积累,我以后不会为了那个坑爹的“持之以恒”的图标来逼迫自己每周一篇了。

今后我会尽量选好一个主题,然后细致的研究学习,确保自己真的明确了以后再发表出来。

利用|,&amp;,^,~,&lt;&lt;,&gt;&gt;&gt;写出高效艺术的代码的更多相关文章

  1. (转)Python新手写出漂亮的爬虫代码1——从html获取信息

    https://blog.csdn.net/weixin_36604953/article/details/78156605 Python新手写出漂亮的爬虫代码1初到大数据学习圈子的同学可能对爬虫都有 ...

  2. MySQL写出高效SQL

    mysql设计标准事务处理标准索引使用标准约束设计sql语句标准 怎么写出高效SQL清晰无误的了知业务需求满足业务需求,不做无用功知道表数据量和索引基本情况知道完成SQL需要扫描的数据量级SQL执行计 ...

  3. 《数据结构与算法之美》 <05>链表(下):如何轻松写出正确的链表代码?

    想要写好链表代码并不是容易的事儿,尤其是那些复杂的链表操作,比如链表反转.有序链表合并等,写的时候非常容易出错.从我上百场面试的经验来看,能把“链表反转”这几行代码写对的人不足 10%. 为什么链表代 ...

  4. 面试刷题28:如何写出安全的java代码?

    对jdk,jvm,java应用程序的攻击多种多样?那么从java程序员的角度,如何写出安全的代码呢? 我是李福春,我在准备面试,今天的题目是:如何写出安全的java代码? 答:这个需要从功能设计到实现 ...

  5. 如何用java写出无副作用的代码

    搞java的同学们可能对无副作用这个概念比较陌生,这是函数式编程中的一个概念,无副作用的意思就是: 一个函数(java里是方法)的多次调用中,只要输入参数的值相同,输出结果的值也必然相同,并且在这个函 ...

  6. 如何写出优雅的CSS代码 ?(转)

    对于同样的项目或者是一个网页,尽管最终每个前端开发工程师都可以实现相同的效果,但是他们所写的代码一定是不同的.有的优雅,看起来清晰易懂,代码具有可拓展性,这样的代码有利于团队合作和后期的维护:而有的混 ...

  7. fir.im Weekly - 如何写出零 bug 的代码

    神兽护体,代码无bug.经常看到代码注释的各种形状,这是一种程序员情怀.那么,如何能写出零 Bug 的代码呢,来看看@码农翻身 的这篇手册--零Bug的代码是怎么炼成的. 写零 Bug 一定少不了代码 ...

  8. 如何写出优雅的css代码 ?

    如何写出优雅的css代码 ? 对于同样的项目或者是一个网页,尽管最终每个前端开发工程师都可以实现相同的效果,但是他们所写的代码一定是不同的.有的优雅,看起来清晰易懂,代码具有可拓展性,这样的代码有利于 ...

  9. 用6个字符写出任意的Javascript代码

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:用6个字符写出任意的Javascript代码.

随机推荐

  1. 用php当作cat使用

    今天,本来是想敲 node test.js 执行一下,test.js文件,结果 惯性的敲成了 php  test.js, 原文输出了 test.js的内容. 突然觉得,这东西 感觉好像是 cat  命 ...

  2. Cognos开发ContentManagerServiceStub不能转换为Stub

    Cognos SDK开发过程中遇到的小错误详细请看下图 另:附加了详细的错误信息 Exception in thread "main" java.lang.ClassCastExc ...

  3. windows live writer首行缩进问题的解决

    使用live writer写博客的确方便,但有个简单的问题,我始终无法解决,就是发布的博客老是无法首行缩进,试过好多方法,都有问题: 直接加全角空格.上传时就给过滤掉了. 修改defaultcss,结 ...

  4. RaceWeb介绍(7):由500强公司数据高速生成百度地图——生成坐标字段及坐标数据

    接上篇. 一.生成X坐标.Y坐标两个字段. 我们须要为每一个公司建立X坐标和Y坐标字段,用来保存XY坐标. 既然为了突出"快",这一步就有程序来完毕吧. 右键单击"世界5 ...

  5. UNIX网络编程读书笔记:名字与地址转换

    概述 在名字和数值地址间进行转换的函数: gethostbyname和gethostbyaddr:在主机名字与IPv4地址之间进行转换.仅仅支持IPv4. getservbyname和getservb ...

  6. TP框架M方法 create方法丢失字段问题

    TP框架M方法 create方法丢失字段问题! thinkphp框架M方法 create方法丢失字段问题! thinkphp框架M方法 add方法字段丢失问题! 数据库 表新增了字段,用create方 ...

  7. JAVA生成解析二维码

    package com.mohe.twocode; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.B ...

  8. Python 倒叙切片

    倒序切片 对于list,既然Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片,试试: >>> L = ['Adam', 'Lisa', 'Bart', 'Paul' ...

  9. oracle 存储过程 示例

      oracle 存储过程 示例 CreationTime--2018年9月4日09点49分 Author:Marydon 1.情景展示 对VIRTUAL_QRCODELOG表的静态二维码,动态二维码 ...

  10. Linux 内核源码情景分析 chap 2 存储管理 (四)

    物理页面的使用和周转 1. 几个术语 1.1 虚存页面 指虚拟地址空间中一个固定大小, 边界与页面大小 4KB 对齐的区间及其内容 1.2 物理页面 与虚存页面相对的, 须要映射到某种物理存储介质上面 ...