在增长、收缩和克隆其值之后,Java位集的大小是多少?

在增长、收缩和克隆其值之后,Java位集的大小是多少?,java,memory-management,clone,bitset,Java,Memory Management,Clone,Bitset,让它成为Java中的bs1位集。 第一位设置为bs1。设置(0),然后其大小和长度分别为64和1。 设置第65位为bs1。设置(64),则其大小和长度分别为128和65 现在,如果我清除它的第65位bs1.clear(64),它的长度会回到1,但是它的大小会怎样呢? 如果克隆位集会发生什么?新版本的大小是默认的吗?更少的单词和更多的代码,在我看来,这个实验回答了这个问题: import java.util.BitSet; public class BitSetExperiment {

让它成为Java中的
bs1
位集。
第一位设置为
bs1。设置(0)
,然后其
大小和
长度分别为64和1。
设置第65位为bs1。设置(64)
,则其
大小和
长度分别为128和65

现在,如果我清除它的第65位bs1.clear(64)
,它的
长度会回到1,但是它的大小会怎样呢?

如果克隆位集会发生什么?新版本的大小是默认的吗?

更少的单词和更多的代码,在我看来,这个实验回答了这个问题:

    import java.util.BitSet;

public class BitSetExperiment {
    public static void main(String[] args) {
        BitSet bs0=new BitSet();
        BitSet bs1;
        System.out.println("created:\tLength,Size bs0: "+bs0.length()+" , "+bs0.size());
        bs0.set(15);
        System.out.println("set(15):\tLength,Size bs0: "+bs0.length()+" , "+bs0.size());
        bs0.set(63);
        System.out.println("set(63):\tLength,Size bs0: "+bs0.length()+" , "+bs0.size());
        bs0.set(86);
        System.out.println("set(86):\tLength,Size bs0: "+bs0.length()+" , "+bs0.size());
        bs0.clear(86);
        System.out.println("clear(86):\tLength,Size bs0: "+bs0.length()+" , "+bs0.size());
        bs0.clear(63);
        System.out.println("clear(63):\tLength,Size bs0: "+bs0.length()+" , "+bs0.size());

        System.out.println("Cloning to bs1...\n");
        bs1=(BitSet)bs0.clone();
        System.out.println("Length,Size bs0: "+bs0.length()+" , "+bs0.size());
        System.out.println("Length,Size bs1: "+bs1.length()+" , "+bs1.size());
    }
}
输出:

created:    Length,Size bs0: 0 , 64
set(15):    Length,Size bs0: 16 , 64
set(63):    Length,Size bs0: 64 , 64
set(86):    Length,Size bs0: 87 , 128
clear(86):  Length,Size bs0: 64 , 128
clear(63):  Length,Size bs0: 16 , 128
Cloning to bs1...

Length,Size bs0: 16 , 64
Length,Size bs1: 16 , 64
查看输出,我发现了两件事:

  • 当您清除更重要的位时,位集不会调整其大小,但可能是使用另一种更复杂的机制完成的,正如蜘蛛@Boris在其评论中所建议的那样
  • 克隆位集时,原始位集和克隆位都以尽可能小的分配表示(以64位的系数表示)

  • 答案可能取决于实施情况。以下信息是通过阅读Java11源代码收集的

    指定用于收缩
    位集的唯一API方法是
    trimToSize()

    大多数(变异的)API方法可能会增加它的大小,但不会减少它

    例外情况是用于Java对象序列化的
    clone()
    和(private)
    readObject
    writeObject
    方法。这些方法的行为取决于名为
    sizeistick
    的私有字段,而该字段又取决于对象以前的历史记录

    如果
    位集
    是使用容量创建的,则它具有粘性大小。除非
    位集
    超出其容量,否则大小将保持不变

    • 克隆具有粘性大小的
      位集时,克隆对象具有相同的大小,并且大小也是粘性的

    • 克隆具有非粘性大小的
      位集时,首先修剪对象,然后创建克隆。克隆的大小将是非粘性的

    • 当使用粘性大小序列化
      位集
      时,序列化表单将保留该大小

    • 使用非粘性大小序列化
      位集
      时,
      位集
      将在序列化之前进行修剪

    • 从序列形式反序列化
      位集
      时,其大小与序列形式的大小相同,并且粘性是试探性设置的

    当大小为非粘性时,
    clone()
    writeObject
    中发生的修剪有点令人惊讶,但(Java 11)代码就是这样做的。
    sizeisticky
    字段上的注释部分解释了该行为

    /**
     * Whether the size of "words" is user-specified.  If so, we assume
     * the user knows what he's doing and try harder to preserve it.
     */
    private transient boolean sizeIsSticky = false;
    

    如果在边界翻转一点时,
    位集
    修剪了其大小,则会降低性能。如果
    位集
    要自动调整其大小,则可能会根据-进行调整,这比您的测试建议的要复杂一点。完全同意,Java很可能会实际调整位集,但不是在清除空间后立即进行。我将在答案中说明这种可能性