OpenJDK 源代码阅读之 BitSet
2014-06-07 19:42
323 查看
概要
类继承关系java.lang.Object java.util.BitSet
定义
public class BitSet extends Object implements Cloneable, Serializable
要点
BitSet类用来支持位操作,给它一个
size,就会返回一个对象,代表
size个位。可以完成“与或非”操作。
实现
试想一下,long最多也就 64 位,假如我们想对 1000 位进行一些运算,要如何实现呢?这个类就告诉我们怎么用一个数组,去实现位操作。
数据
private long[] words;
内部使用
long类型的数组来存储数据。
初始化
public BitSet(int nbits) { // nbits can't be negative; size 0 is OK if (nbits < 0) throw new NegativeArraySizeException("nbits < 0: " + nbits); initWords(nbits); sizeIsSticky = true; } private void initWords(int nbits) { words = new long[wordIndex(nbits-1) + 1]; } private static int wordIndex(int bitIndex) { return bitIndex >> ADDRESS_BITS_PER_WORD; }
初始化会根据的位数决定要申请多大的数组,
long类型是 64 位,所以你如果
nbits是
1~64,你只需要一个长度为1的数组就好。
扩充策略
要是数组不够用了,就要进行扩充,下面的函数会根据申请的
long元素个数,经过与当前元素个数2倍的比较进行扩充。
private void ensureCapacity(int wordsRequired) { if (words.length < wordsRequired) { // Allocate larger of doubled size or required size int request = Math.max(2 * words.length, wordsRequired); words = Arrays.copyOf(words, request); sizeIsSticky = false; } }
位翻转
public void flip(int bitIndex) { if (bitIndex < 0) throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); int wordIndex = wordIndex(bitIndex); expandTo(wordIndex); words[wordIndex] ^= (1L << bitIndex); recalculateWordsInUse(); checkInvariants(); }
先根据索引位置
bitIndex计算出相应的位在数组哪个元素里,然后再将 1 左移
bitIndex位后与此元素作异或运算。注意
bitIndex如果超过了
64 位,会又循环回来,比如
1L << 69其实和
1L << 5是一样的,只不过异或的时候,一个与
words[1]异或,一个与
words[0]。
类中还有其它位操作,比如置1,清0,只是和
flip的位操作符不同。
还有一类是区间内翻转,这需要首先临到一个相应区间全为1的数字,再与
words相应元素作运算。
public void flip(int fromIndex, int toIndex) { checkRange(fromIndex, toIndex); if (fromIndex == toIndex) return; int startWordIndex = wordIndex(fromIndex); int endWordIndex = wordIndex(toIndex - 1); expandTo(endWordIndex); long firstWordMask = WORD_MASK << fromIndex; long lastWordMask = WORD_MASK >>> -toIndex; if (startWordIndex == endWordIndex) { // Case 1: One word words[startWordIndex] ^= (firstWordMask & lastWordMask); } else { // Case 2: Multiple words // Handle first word words[startWordIndex] ^= firstWordMask; // Handle intermediate words, if any for (int i = startWordIndex+1; i < endWordIndex; i++) words[i] ^= WORD_MASK; // Handle last word words[endWordIndex] ^= lastWordMask; } recalculateWordsInUse(); checkInvariants(); }
如果区间跨越多个数组元素,还需要把中间的数个数组元素内容全部翻转。
AND 操作
public void and(BitSet set) { if (this == set) return; while (wordsInUse > set.wordsInUse) words[--wordsInUse] = 0; // Perform logical AND on words in common for (int i = 0; i < wordsInUse; i++) words[i] &= set.words[i]; recalculateWordsInUse(); checkInvariants(); }
从这个函数体会一下,两个
BitSet对象之间的
AND操作如何进行,其实就是对应的数组元素之间作
AND操作就行。
hashCode
public int hashCode() { long h = 1234; for (int i = wordsInUse; --i >= 0; ) h ^= words[i] * (i + 1); return (int)((h >> 32) ^ h); }
计算哈希值的操作,说实话,我是不太明白为什么这样算哈希值的,为什么这样能减少不同
BitSet之间的碰撞呢?
剩下的东西我也不想分析了,总之,需要把握整体的思路,就是如何用一个数组去实现位操作,每次操作需要弄清楚,在数组的哪些元素上操作,与什么数字作位操作,做什么位操作。
相关文章推荐
- OpenJDK 源代码阅读之 Arrays
- OpenJDK 源代码阅读之 String
- OpenJDK 源代码阅读之 TreeMap
- OpenJDK 源代码阅读之 ArrayList
- OpenJDK 源代码阅读之 HashMap
- OpenJDK 源代码阅读之 LinkedList
- OpenJDK 阅读源代码 Java 实现字节流输入类
- OpenJDK 源代码阅读之 Collections
- OpenJDK 源代码阅读之 TimSort
- 关于阅读源代码的误区《代码阅读方法与实践》
- ubuntu下使用自带的openJDK查看java源代码
- 怎么阅读源代码
- OS161 源代码阅读-2
- Code Browser-源代码阅读工具
- Ubuntu 下安装LXR(linux源代码阅读工具)
- Linux下阅读源代码
- Java源代码阅读——Object类
- Lua源代码阅读(五)数据栈与调用栈组成的 线程(协程)
- Linux下源代码阅读工具
- OpenJDK 源代阅读之 ArrayDeque