范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

位运算java集合源码体系研究之基础,让你面试胖揍面试官

  源码分享说明 本人非常热衷于源码研究,同时也非常愿意将自己在源码方面研究的心得进行分享,如果读者也想对源码进行研究,可以关注我分享的文章。 在进行源码解析的过程中,将会选择庖丁解牛式剖析,将会解析清楚每一行核心代码,让读者能够真正透彻理解,这样无论是在以后的面试中还是独立开发中间件都会有很大好处。
  序言 这个系列主要分享Java集合类源码,由于Java集合中每一个容器类都是基于特定的数据结构实现。其实我本人也是直接就分享源码,但是如果读者本身的基础不是很好,导致最终分享的效果不是很好,只能退而求其次,通过基础系列补充对应的数据结构方面的基础,当然在数据结构内容的选取方面,只会选取跟Java集合类相关的东西,数据结构基础越扎实,阅读源码的效果就越好。 数据在计算机中是以补码的形式存在,并且一般都是基于整数,Java集合类中会有大量的位运算,常见的位运算有取反,与,或,异或,左移,右移这六种,考虑到读者对这方面的知识可能不是很熟悉,所以特地分享这一篇技术文章,首先从概念上进行阐述,为了更好的巩固这些东西,精心选取了几个示例进行讲解,相信能够对读者有些帮助。
  1.数据表示形式
  我们都知道在数字有原码,反码,补码,移码这4中表示形式,但是为了让符号位能够跟数据位参与一样的运算规则,在计算机中使用补码的形式表示数据,同时在Java集合中,参与位运算都是整数,所以着重讲解整数补码操作。
  把符号"数字化"的数称为机器数,而把带"+"或"-"符号的数称为真值
  原码:符号位为0表示整数,符号位为1表示负数,数值位即真值的绝对值,故原码表示又称为带符号的绝对值表示。
  例如:[+14]原 = 0,1110;[-14]原 = 1,1110;
  反码:
  例如:[+14]反 = 0,1110;[-14]反 = 1,0001;
  补码:
  例如:[+14]补 = 0,1110;[-14]补 = 1,0010;
  综上所述,三种机器数有如下特点 三种机器数的最高位均为符号位。 当真值位正时,原码,反码,补码表示形式相同,即符号位用0表示,数值部分与真值相同。 当真值位负时,原码,反码,补码表示形式不同,但其符号位用1表示,而数值部分关系为反码是原码的每位求反,补码是原码每位求反加1。
  2.位运算
  我们常见的位运算有取反,与,或,异或,左移,右移这六种
  取反:0取反得1,1取反得0
  例如:
  ~0000 1010 = 1111 0101;
  ~1000 1010 = 0111 0101;
  与,或,异或
  与(&)
  0&0=0
  0&1=0
  1&0=0
  1&1=1
  或(|)
  0|0=0
  0|1=1
  1|0=1
  1|1=1
  异或(^)
  0^0=0
  0^1=1
  1^0=1
  1^1=0
  其实对于异或运算,你可以理解为无进位相加
  例如:
  0 ^ 0 = 0 ==>> 0 + 0 = 0;
  0 ^ 1 = 1 ==>> 0 + 1 = 1;
  1 ^ 0 = 0 ==>> 1 + 0 = 1;
  1 ^ 1 = 0 ==>> 1 + 1 = 10 舍弃进位1就得到1 + 1 = 0;
  左移运算符
  m << n表示把m左移n位。如果左移n位,那左端的n位将会被丢弃,同时在最右边补上n个0
  例如:
  0000 1010 << 2 = 0010 1000;
  1000 1010 << 3 = 0101 0000;
  无符号右移运算符
  m >>> n表示把m无符号右移n位。如果无符号右移n位,那右端的n位将会被丢弃,同时在最左边补上n个0
  例如:
  0000 1010 >>> 2 = 0000 0010;
  1000 1010 >>> 3 = 0001 0001;
  右符号右移运算符
  m >> n表示把m有符号右移n位。如果有符号右移n位,那右端的n位将会被丢弃,同时在最左边补上n个符号位
  例如:
  0000 1010 >> 2 = 0000 0010;
  1000 1010 >> 3 = 11111 0001;
  位运算尽管定义得非常简洁,但是如果你使用的好,威力将是无穷,举上几例让你们感受一下,对于例题中给出的结论要牢牢记住并且能够做到融会贯通。
  3.实例操作
  结论:n & (n-1)将n最右端的1变为0
  例1:给定一个32位整数n,可为0,可为正,也可为负,返回该整数二进制表达式中1的个数
  首先分析一下n & (n-1)这个位运算的结果
  为了便于直观,我们先随机取一个数n = 0100 0100,n - 1 = 0100 0011
  n & (n-1) = 0100 0100 & 0100 0011 = 0100 0000 即将最右端的1变为0
  其实这个结论也是非常容易证明,试证一下,假如n最右端1出现在i位上,这样n - 1的结果体现为:i位变成0,i位前面不变,i位后面全部变成了1,这样n & (n-1)的结果就是i位变成0,同时i位后面本来就是0,所以没有影响。从而证明了n & (n-1)将n最右端的1变为0
  有了如上这个结论 n &= (n-1)可理解为将n最右端的1变为0再赋值给n public int resolve(int num){      int result = 0;        //每一次循环都去掉num中最右端的1      //等所有num中所有1都去掉之后      //num = 0结束循环,得到num中1的个数      while(num != 0) {          //去掉num中最右端的1         num &= (num - 1);               result++;       }        return result; }
  结论:n & (-n)得到n最右端第一次出现1的位置
  根据补码的结论有 -n = ~n + 1
  例2:在一个数字序列中,有两个数出现了奇数次,其他数都出现了偶数次,寻找这两个数。
  异或运算符合两个性质:交换律和结合律,也就是说
  a ^ b = b ^ a;
  a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
  其实这两个性质我们可以统一来证明一下,我们上面说到异或运算,可以理解为无进位相加,有了这个上面两条性质就很好证明了a ^ b的结果就是a转化为二进制和b转化为二进制,按对应每一个二进制位相加组合而得到,自然a ^ b = b ^ a。类似可以证明a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c
  异或运算又有如下两个性质,根据异或运算,可以理解为无进位相加很轻易得到
  a ^ a = 0
  a ^ 0 = a
  这样就有如此结论:偶数次异或为0,奇数次异或为本身
  下面证明结论:n & (~n + 1)得到n最右端第一次出现1的位置
  假如n最右端1出现在i位上,~n第i位为0,i位前面全部取反,i位后面全部变成了1,加上1之后,第i位重新变为1,i位后面全部变成了0,在跟n进行&运算,由于在i位前面,n与-n全部相反,自然&运算之后全部为0,~n第i位后面全部变成了0,与任何数&运算之后也全部为0,同时
  n和-n第i位都是1,所以&运算之后第i位还是1,证毕。 //假如两个数出现了奇数次分别为m,n public void resolve(int[] arr){     int res_one = 0;             for(int value:arr){         res_one ^= value;     }     //经过上面异或运算之后res_one = m ^ n     //由于m != n,所以res_one肯定不为0     //res_one二进制下必然有某一位为1,这里选择最右边的1        //上面我们证明了通过 res_one & (~res_one + 1)得到最右端为1的位     //假如最右端第i位为1,则m,n在第i位必然有一个为1,另一个为0     //不然与res_one第i位为1相矛盾     int right_one = res_one & (~res_one + 1);     int res_two = 0;     for(int value:arr){         //将数组中第i位为1和为0的元素分开         //假如m第i位为1,n第i位为0         //找出第i位为1的元素跟res_two异或         //整个循环结束之后res_two就是m的值         if((value & right_one) != 0) {             res_two ^= value;         }     }     //res_one = m ^ n,res_two = m,     //res_one^res_two = m ^ n ^ m = n     System.out.println(res_two+" " +(res_one^res_two)); }
  例3:位图
  每个元素为"0"或"1",表示其对应的元素不存在或者存在。为了表示某个数字存在,将对应索引位的bit值置为1。
  如果使用char M [N]这种结构存储,可以存储 N * 8个数据,但是最大的数只能是
  N * 8 - 1。
  例如我们要存储数据范围为0-15,则只需要使得N=2,就可以把数据存进去。
  如果我们需要使用位图表示数据【5,1,7,15,0,4,6,10】,只需要将位图中【5,1,7,15,0,4,6,10】对应bit位的值变为1,最终位图结构中如下所示
  下面代码示例了使用位图进行添加操作,至于其它的操作(删除/检索)都是类似,没有书写对应代码,因为觉得没有必要,毕竟本意是让读者能够更好的理解位运算。 public class BitMap {     private long[] bits;             //一个long有64个bit位,根据构造时传入的最大值     // 决定需要多少个连续的long值存储     public BitMap(int max) {     // 如果是一个数字,即使这个数字是0,也需要申请一个。     // n >> 6 是将n除以64进行向上取整     bits = new long[(max + 64) >> 6];     }          public void add(int num) {         //为了更好的理解下面这行代码的逻辑         //我们选择举一个例子直观说明         // 例如需要添加数字170         //我们需要知道long[]哪个索引的long负责存储         // 0位索引long负责整数:0-63(第一位管0,第63位管64)         // 1位索引long负责整数:64-127         // 2位索引long负责整数:128-191         //170在这个范围之内,就使用2位索引long存储         //现在我们已经找到哪个索引long存储         //还需要知道170处于这个long中哪个bit位中         //前面2个long存储了128个bit         //170对应在第3个long中bit位为170-128=42              //对应的操作已经说清楚了,现在需要说明怎样通过代码实现                  //1.得到long[]哪个索引的long负责存储         //将给定数字/64进行向上取整就可以,转换为位运算就是给定数字>>6         //2.得到long[]对应索引long中的bit位         //特此说明一下在n为2的指数次幂-1就会num % n = num & n         //这个在hashmap中也使用了这个性质,其实证明也很简单,读者可以尝试证明         // num % 64 就是 num & 63,也就是把小于64的数字都取出来,这就是余数         // 63 的二进制:(这里还有32位) 00000000 00000000 00000000 000111111         // 170的二进制:(这里还有32位) 00000000 00000000 00000000 010101010         // 结果:(这里还有32位) 00000000 00000000 00000000 000101010         bits[num >> 6] |= (1L << (num & 63));     } }

13代英特尔CPU将涨价20,市值彻底被AMD反超Intel作为计算机CPU领域扛把子,不论是服务器端还是消费端,都占据着超过70的市场份额。但近日,Intel公布了2022年Q2季度财报,着实让人大跌眼镜。财报显示,Intel今这两款手机性价比超高,各方面都不错,很推荐入手第一款vivoS15pro推荐指数这款手机在拍照能力性能外观辨识度和续航表现等多个方面,均是有着不错的表现的处理器联发科天玑8100RAM存储类型LPDDR5四通道ROM存储类型U新一代华为全屋智能正式首销引领交互革命,解锁后装空间近日,新一代华为全屋智能正式亮相,新品重点针对交互体验进行全方位升级,同时还带来了全新的后装解决方案,能够覆盖更多户型,即便是精装房也可考虑入手,实现了在交互空间生态方面的升级,进华纳大乱斗离线模式掉线连接已丢失的解决办法Multiversus华纳大乱斗又称多元宇宙大乱斗,该作拥有华纳旗下众多经典IP的版权,在国内外的玩家人数也是一直居高不下。不过由于其服务器假设在国外,且对国内锁区,所以大家在日常女性机器人广受欢迎,但却面临3大问题,男性坦言不敢用女性机器人的横空出世广受大家欢迎,尤其是采用了硅胶制作的美女机器人,其外貌酷似真人,甚至比大多数美女还要美上几分,所以成为许多宅男的新宠。女性机器人无论是体温模仿还是皮肤触感,都非收评A股8月开门红创业板指大涨2。37,机器人概念汽车零部件板块掀涨停潮金融界8月1日消息周一A股三大指数低开,早盘市场并于盘初短暂下探后震荡反弹,创业板指率先翻红并涨超2,此前曾跌超1,沪指午前翻红小幅上涨午后A股呈现震荡整理态势,创业板指全天领涨。夏天要多喝热水,提醒中老年人热水里加点它,补充营养还健康多喝热水,世界上本没有这句话,说的人多了便有了它的存在。不是只有敷衍的时候才会说多喝热水,表示关心的时候也会说多喝热水。多喝热水对身体确实有很大的好处,尤其是夏天。在抗美援朝时期,伏天燥热易上火,别乱吃,建议多给家人吃这6样,营养解暑不上火中伏天正值一年中最热的季节,也是人体最容易上火的时候。伏天是一年中最热的时候,各种极端高温天气都会在这段时间出现,很多人特别容易烦躁焦虑易怒,属于暑火,出现脾气暴躁心烦失眠等症状,给下一代让路?iPhone13官方罕见降价引争议,库克正面回应大多数人都认为,苹果可能是唯一没有争议的高端手机品牌。不管是在线上市场,还是线下市场,iPhone总是不愁销量的。线上线下都能通吃,这就是品牌号召力的证明。这是一件非常罕见的事,明2000元手机推荐,中端机越级使用旗舰芯,第一名不愧是卷王手机性能决定了体验的上限与下限,可以做什么不能做什么!全都要看处理器,而且一款手机能否流畅使用三年或者更久,核心因素也和处理器有关。所以买手机,别总盯着外观和品牌去看,处理器才是重国风在此间升腾从国内自研的武侠竞技游戏永劫无间,掀起国际友人东风武侠的热潮,再到法国Sloclap工作室发行的独立游戏师父,以中国传统功夫为主题,仅发售三周便实现全球销量破百万近年来,中华传统文
宝可梦如果抛弃纯输出路线,逐电犬是否就有可玩性?八代对战环境如今已经是在成熟的阶段了,而一些八代的冷门宝可梦也慢慢淡出了玩家的视野,成为了八代的新冷板凳存在,逐电犬就是其中之一,这只宝可梦的优势就在于有着121的基础速度,但是,荣耀60Pro一台不做性能却把其他方面做到最好的手机说到荣耀,可能有很多人都知道他最初是华为推出来和小米打持久战的,可从华为哪里独立品牌出去后,它越来越像多年前的ov。一台手机并不是只看性能,所以荣耀60Pro它来了。荣耀60Pro如何玩转这张地图?首先得从这些英雄入手了解全貌才能把控全局最近令小鸡上头的地图可真是有些多,昨天刚给大家分析完了最近比较上头的一张塔防地图,但众所周知,RPG地图类型可不止这一家,而最近在防守地图区块最为令小鸡上头的当属这张斩魔登仙2。也盘点最赚钱的网游,这些游戏你玩个几个?一,梦幻西游于2003年12月8号正式上线,距今差不多已经18年依旧热度不减,是搬砖玩家的首选游戏,前期需要一定的投入,5开175或者129的账号,后面可以边挂机边看电影,而且有自人偶情缘2MAC苹果电脑游戏繁体中文版支援10。1310。1410。151112人偶情缘2MAC苹果电脑游戏繁体中文版支援10。1310。1410。151112适用于APPLECPU游戏编号345游戏类型恋爱养成游戏大小0。68Gb支持系统macOS10。13科普冬季慎防心脑血管病冬季慎防心脑血管病寒冷的冬季,对患有心脑血管病的人来说,是一种威胁。大量临床资料表明寒冷的气候,特别是当遭受强冷空气袭击气温变化剧烈时,冠心病中风等心脑血管病的发病率明显提高,病人5位拿不上台面的过气明星,趁早改行经商,一块手表上千万娱乐圈向来就是一个竞争不小的名利场,曾经无论多么风光的明星大腕,随着娱乐圈的更新换代,不免会成为过气明星,这些人看似在娱乐圈没了地位,实际私下早已看透事实,成了众人难已逾越的富商。詹姆斯韦伯太空望远镜完成了镀金主镜的展开顺利完成了主要部署工作詹姆斯韦伯太空望远镜(JWST)已经完成了其主镜的展开,从而结束了在两周时间内进行的一系列主要部署工作。所有这些部署都需要完美地进行,以使这个耗时数十年的大型太空望远镜能够发挥作用为什么詹姆斯韦伯太空望远镜上没有相机?为什么詹姆斯韦伯太空望远镜上没有相机?由于许多实际问题,工程师们只能凑合着用传统的遥测技术。公众现在已经习惯了近距离观察太空,这要归功于摄像机观察一切,从部署卫星到穿着宇航服的特斯游戏王卡图故事圣像骑士对战自奏圣乐,曾经的战友如今成仇敌因为自奏圣乐军团的存在,奥拉姆以自己的力量无法进入通天塔内。他原本想着与宁吉尔苏进行当面对质,说明星遗物会带来世界毁灭的结局,到时候即便复活夏娃也无济于事。然而现在的情况却是,他连推荐10款Steam冬季特卖高质量游戏,史低价还没一杯奶茶贵Steam冬季特卖目前已经进行到了第四天,大家都入手了什么游戏?如果还在犹豫的话,可以看看这10款高质量游戏,目前价格都到达了史低价,还没一杯奶茶贵。方舟生存进化,33折,29元方