博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenJDK 源代码阅读之 BitSet
阅读量:6947 次
发布时间:2019-06-27

本文共 3303 字,大约阅读时间需要 11 分钟。

概要

  • 类继承关系
java.lang.Object    java.util.BitSet
  • 定义
public class BitSetextends Objectimplements 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 之间的碰撞呢?

剩下的东西我也不想分析了,总之,需要把握整体的思路,就是如何用一个数组去实现位操作,每次操作需要弄清楚,在数组的哪些元素上操作,与什么数字作位操作,做什么位操作。

转载于:https://www.cnblogs.com/Iambda/p/3933459.html

你可能感兴趣的文章
第三周Java学习总结
查看>>
OGRE的安装和编译【转+改】
查看>>
获取管理员组用户
查看>>
Mysql—(2)—
查看>>
简历的分布式
查看>>
[转]string和stringstream用法总结
查看>>
LeetCode:Rotate Array
查看>>
jquery pagination.js 分页
查看>>
DOM对象与jquery对象
查看>>
1.6(SQL学习笔记)存储过程
查看>>
XXS level8
查看>>
分布式日志收集系统:Facebook Scribe
查看>>
VxWorks下PCI驱动的配置与测试
查看>>
NSString 中包含中文字符时转换为NSURL
查看>>
Unity 协程停不了?
查看>>
多个文本框点击复制 zClip (ZeroClipboard)有关问题
查看>>
5款手机应用开发框架推荐
查看>>
Gawker攻击事件暴露密码保护缺陷
查看>>
16.ARC
查看>>
OpenCV——ANN神经网络
查看>>