Java-API简析_java.util.BitSet类(基于 Latest JDK)(浅析源码)

BitSet是Java中用于存储和操作位集合的数据结构,它可以进行逻辑与、或、非和异或等操作。文章介绍了BitSet的构造方法,如无参数构造和指定大小构造,以及各种方法,如设置、清除、翻转位,计算真值个数,交集、并集和差集等操作。
摘要由CSDN通过智能技术生成

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/130457413
出自【进步*于辰的博客

参考类:Integer

数据结构:位图

启发博文:《Java 中 BitSet》(转发)。

此类是Java中“位图”数据结构的体现,主要用于减少空间的占用。

有点抽象,我引用一张启发博文中的简图:
在这里插入图片描述

这个“long数组”指的就是words

我通过我的理解为大家画一张更细致的草图:
在这里插入图片描述

在下文【属性说明】中可以看到,words的类型是long[],每个元素由8个字节表示,计64位,每一位对应一个数字的“存在状态”(0/1),数字从0开始依次对应。

上图中,为何“对应数字”是“倒序”的?因为灰色框内是每个元素(64位)的二进制补码,在设置(将某一位修改为1)时,底层逻辑是:(以第3.20项为例)

words[wordIndex] |= (1L << bitIndex);

因此,上图示例中words存储的是6465的“存在状态”,words[0, 3]

举个栗子:

BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}

怎么是[64, 65]?我再获取一下words

Field f1 = BitSet.class.getDeclaredField("words");
f1.setAccessible(true);
sout Arrays.toString((long[]) f1.get(set));// [0, 3]

所以,将[0, 3]变为[64, 65],归功于toString()(见第3.25项),源码我暂未解析,其底层逻辑无非是“遍历计数”。

1、概述

继承关系:

  • java.lang.Object
    • java.util.BitSet

所有已实现的接口:
Serializable, Cloneable


public class BitSet extends Object implements Cloneable, Serializable

此类实现了一个按需增长的位向量。位 set 的每个组件都有一个 boolean 值。用非负的整数将 BitSet 的位编入索引。可以对每个编入索引的位进行测试、设置或者清除。通过 逻辑与、逻辑或逻辑异或 操作,可以使用一个 BitSet 修改另一个 BitSet 的内容。

默认情况下,set 中所有位的初始值都是 false

每个位 set 都有一个当前大小,也就是该位 set 当前所用空间的位数。注意,这个大小与位 set 的实现有关,所以它可能随实现的不同而更改。位 set 的长度与位 set 的逻辑长度有关,并且是与实现无关而定义的。

除非另行说明,否则将 null 参数传递给 BitSet 中的任何方法都将导致 NullPointerException在没有外部同步的情况下,多个线程操作一个 BitSet 是不安全的

从以下版本开始:
JDK1.0
另请参见:
序列化表格

2、构造方法摘要

属性说明:

private long[] words;

// 表示“长度”是否是用户指定的,暂不知何用
private transient boolean sizeIsSticky = false

// 已使用“单词”的个数(逻辑长度)
private transient int wordsInUse = 0;

字段说明:

private final static int ADDRESS_BITS_PER_WORD = 6;
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;// 64
private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;// 63,暂不知何用

private static final long WORD_MASK = 0xffffffffffffffffL;// -1L,暂不知何用

2.1 null

创建一个新的位 set

public BitSet() {
    initWords(BITS_PER_WORD);// 初始化,见第4.1项
    sizeIsSticky = false;
}

构造出的words[0]

2.2 int nbits

创建一个位 set,它的初始大小足以显式表示索引范围在 0nbits - 1 的位。

public BitSet(int nbits) {
    if (nbits < 0)
        throw new NegativeArraySizeException("nbits < 0: " + nbits);

    initWords(nbits);
    sizeIsSticky = true;
}

3、方法摘要

3.1 void and(BitSet set)

对此目标位 set 和参数位 set 执行逻辑与操作。

3.2 void andNot(BitSet set)

清除此 BitSet 中所有的位,其相应的位在指定的 BitSet 中已设置。

3.3 int cardinality()

返回此 BitSet 中设置为 true 的位数。

public int cardinality() {
    int sum = 0;
    for (int i = 0; i < wordsInUse; i++)
        sum += Long.bitCount(words[i]);
    return sum;
}

bitCount()的作用是返回二进制补码表示形式的 1 位的数量,可参考Integer类的第4.1项。

3.4 void clear()

将此 BitSet 中的所有位设置为 false

public void clear() {
    while (wordsInUse > 0)
        words[--wordsInUse] = 0;
}

3.5 void clear(int bitIndex)

将索引指定处的位设置为 false

3.6 void clear(int fromIndex, int toIndex)

将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 false。

3.7 Object clone()

克隆此 BitSet,生成一个与之相等的新 BitSet。

3.8 boolean equals(Object obj)

将此对象与指定的对象进行比较。

3.9 void flip(int bitIndex)

将指定索引处的位设置为其当前值的补码。

3.10 void flip(int fromIndex, int toIndex)

将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的每个位设置为其当前值的补码。

3.11 boolean get(int bitIndex)

返回指定索引处的位值。

3.12 BitSet get(int fromIndex, int toIndex)

返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成。

3.13 int hashCode()

返回此位 set 的哈希码值。

3.14 boolean intersects(BitSet set)

如果指定的 BitSet 中有设置为 true 的位,并且在此 BitSet 中也将其设置为 true,则返回 true

3.15 boolean isEmpty()

如果此 BitSet 中没有包含任何设置为 true 的位,则返回 ture。

3.16 int length()

返回此 BitSet 的“逻辑大小”:BitSet 中最高设置位的索引加 1。

public int length() {
    if (wordsInUse == 0)
        return 0;

    return BITS_PER_WORD * (wordsInUse - 1) +
        (BITS_PER_WORD - Long.numberOfLeadingZeros(words[wordsInUse - 1]));
}

numberOfLeadingZeros()的作用是返回二进制补码表示形式中最高位的 1 位之前0位的个数,可参考Integer类的第4.15项。

示例:

BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}

Field f1 = BitSet.class.getDeclaredField("wordsInUse");
f1.setAccessible(true);
sout f1.get(set);// 2

sout set.length();// 66

3.17 int nextClearBit(int fromIndex)

返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。

示例:

BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}

sout set.nextClearBit(63);// 63
sout set.nextClearBit(64);// 66

3.18 int nextSetBit(int fromIndex)

返回第一个设置为 true 的位的索引,这发生在指定的起始索引或之后的索引上。

示例:

BitSet set = new BitSet();
set.set(64);
set.set(65);
sout set;// {64, 65}

sout set.nextSetBit(63);// 64
sout set.nextSetBit(64);// 64
sout set.nextSetBit(66);// -1

3.19 void or(BitSet set)

对此位 set 和位 set 参数执行逻辑或操作。

3.20 void set(int bitIndex)

将指定索引处的位设置为 true

public void set(int bitIndex) {
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    int wordIndex = wordIndex(bitIndex);
    expandTo(wordIndex);// 见第4.3项

    words[wordIndex] |= (1L << bitIndex);// ------A

    checkInvariants();// 后续解析
}

注意:A 处涉及一个位运算规则,见博文《二进制相关概念、运算与应用》中的【扩展】一栏。

3.21 void set(int bitIndex, boolean value)

将指定索引处的位设置为指定的值。

public void set(int bitIndex, boolean value) {
   if (value)
       set(bitIndex);// 见上 1项
   else
       clear(bitIndex);// 见第5项
}

3.22 void set(int fromIndex, int toIndex)

将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 true。

3.23 void set(int fromIndex, int toIndex, boolean value)

将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为指定的值。

public void set(int fromIndex, int toIndex, boolean value) {
    if (value)
        set(fromIndex, toIndex);// 见上1项
    else
        clear(fromIndex, toIndex);// 见第6项
}

3.24 int size()

返回此 BitSet 表示位值时实际使用空间的位数。

public int size() {
    return words.length * BITS_PER_WORD;
}

3.25 String toString()

返回此位 set 的字符串表示形式。

public String toString() {
    checkInvariants();

    int numBits = (wordsInUse > 128) ?
        cardinality() : wordsInUse * BITS_PER_WORD;
    StringBuilder b = new StringBuilder(6*numBits + 2);
    b.append('{');

    int i = nextSetBit(0);
    if (i != -1) {
        b.append(i);
        while (true) {
            if (++i < 0) break;
            if ((i = nextSetBit(i)) < 0) break;
            int endOfRun = nextClearBit(i);
            do { b.append(", ").append(i); }
            while (++i != endOfRun);
        }
    }

    b.append('}');
    return b.toString();
}

底层逻辑就是“遍历计数

后续解析。

3.26 void xor(BitSet set)

对此位 set 和位 set 参数执行逻辑异或操作。

4、方法摘要

4.1 private void initWords(int nbits)

初始化。

private void initWords(int nbits) {
    words = new long[wordIndex(nbits-1) + 1];// 见下 1项
}

之所以+1,是因为wordIndex()返回的是“下标”,而+1对应的是“长度”。

至于为何先-1,暂未可知。

4.2 private static int wordIndex(int bitIndex)

返回指定数字对应下标。

private static int wordIndex(int bitIndex) {
    return bitIndex >> ADDRESS_BITS_PER_WORD;
}

因此,下标是bitIndex >> 6words[bitIndex]),索引是bitIndex % 64words[bitIndex]有64位,bitIndex对应bitIndex % 64索引)。

4.3 private void expandTo(int wordIndex)

确保BitSet可以容纳给定的wordIndex,从而暂时违反不变量。调用方必须在返回给用户之前恢复不变量,可能使用recomputeWordsIUse()。(暂不理解什么是“不变量”)

private void expandTo(int wordIndex) {
    int wordsRequired = wordIndex+1;
    if (wordsInUse < wordsRequired) {
        ensureCapacity(wordsRequired);// 见下 1项
        wordsInUse = wordsRequired;
    }
}

后续解析。

4.4 private void ensureCapacity(int wordsRequired)

扩容。

private void ensureCapacity(int wordsRequired) {
    if (words.length < wordsRequired) {
        int request = Math.max(2 * words.length, wordsRequired);
        words = Arrays.copyOf(words, request);
        sizeIsSticky = false;
    }
}

4.5 private void checkInvariants()

变量检验。

private void checkInvariants() {
    assert(wordsInUse == 0 || words[wordsInUse - 1] != 0);
    assert(wordsInUse >= 0 && wordsInUse <= words.length);
    assert(wordsInUse == words.length || words[wordsInUse] == 0);
}

暂且未能完全理解其意图。

最后

如果大家需要Java-API文档,我上传了《Java-API文档-包含5/8/11三个版本》。


本文暂缓更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进步·于辰

感谢打赏!很高兴可以帮到你!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值