首先你去搜索ASCII表,看一下里面对字符的编码。
下面假设字符串为aba,进入代码:
int[] charset = new int[8]:
没什么说的,建立一个长度为8的整形数组,为后续提供容器(注意一点,整形数组默认初始化时,每一个位置的默认值是0)
char[] chars = string.toCharArray():
将字符串拆分成字符数组,拆分完后,得到的应该是{'a', 'b', 'a'};
进入for循环,首先拿到第一个字符a,它对应的编码是97;
int row = c / 32 :
这一步开始就很关键了,这里对32取模;字符a对32取模的值为3。
为什么对32取模?这样做,就把每32个字符分成了一组,同时row的值就充当了组编号,注意,不在同一组内的字符肯定是不会相等的。
int col = c % 32 :
这里对32取余,作用后面会分析;字符a对32取余的值为1 。
为什么对32取余?因为,我们需要标记字符是否出现,这里用了一个非常巧妙的方法,前面说了,对32取模是对字符进行分组,每32个字符一组,现在要在这个组内标记某个字符是否出现,恰好int类型的长度是32bits,一组有32个元素,那么每个比特位就可以作为该位置的字符是否出现的状态标记,这里用1代表出现,0代表未出现。
if ((charset[row] & 1 << col) != 0) :
这里是一个难点,涉及到位运算以及符号优先级问题,这句话实际上等价于if ((charset[row] & (1 << col)) != 0) ,1 << col 会优先执行, 在我们的例子里,字符a得到的row为3,col为1,因此数组 charset的第四个元素被选中,代表a在第4组,1 << col是将1做移位操作,这里是左移1位,变成二进制的10也就是十进制的数字2,前面说了,int的初始默认值是0,所以,这里显然((charset[row] & (1 << col)) != 0)的运算结果等于0,跳到else。
在else 里面 charset[row] |= (1 << col)等价于charset[row] = charset[row] | (1 << col)这代码将 二进制的10也就是十进制的数字2,通过按位或的方式,放到了charset的第四个分组里,代表,已经发现了a字符。
接下来就是循环这个操作,遇到b时,依然是到第四个分组,这时 charset[3]已经等于2了,但是b对32取余等于3,所以(1 << col) 得到的结果是二进制的100也就是十进制的数字4 ,(charset[3] & (1 << 3)) 就是 10 & 100显然,结果还是等于0,因此又走到else, charset[row] |= (1 << col) 就是 charset[3] = 10 | 100 现在为110,代表第4个分组里面 已经发现了a、b两个字符。
在然后到了第二个a,这时候,第4个分组里面是110,1 << 2是10,110 & 10 = 10,不等于0,执行 return String.valueOf(c);,返回a这个字符。