12

Is there any Java library offering an ImmutableBitSet? I didn't find any, neither Guava nor using Google.

palacsint
  • 28,416
  • 10
  • 82
  • 109
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 1
    @Joachim Sauer For the same things I'm using `BitSet` - remembering what's on and what's off. It should be immutable, since it shouldn't get changed after creation and it must be thread-safe. It should have a constructor taking an ordinary `BitSet`. – maaartinus Aug 11 '11 at 10:05
  • 2
    By "what's on and what's off", are we talking about some pre-defined set of features/flags? If so, then an `EnumSet` might be a good solution. While it is not inherently immutable, you can easily make a unmodifiable wrapper using `Collection.unmoidifiableSet()`. – Joachim Sauer Aug 11 '11 at 10:08
  • @Joachim Sauer: I use a couple of such pre-defined sets, they're fixed, but I don't want to convert all of them to enums (too many elements, too much writing, loss of flexibility). – maaartinus Aug 11 '11 at 11:40
  • Too much writing? Sounds like a job for search & replace to me. And what flexibility are you loosing? Well, it's your choice. – Joachim Sauer Aug 11 '11 at 14:37
  • 1
    @Joachim Sauer: Sure, search and replace is not a demanding job. Concerning flexibility: Imagine I need to represent a single deck of canasta cards, so I generate 4 times 13, i.e., 52 cards. I could make such a big enum, but what if the figures change? And they will probably change one day, since what I'm doing is not canasta. – maaartinus Aug 11 '11 at 14:50
  • Yes, enums can have any (sane) size. And adding another enum values is about as much work as adding another constant to an interface. – Joachim Sauer Aug 11 '11 at 14:52
  • @Joachim Sauer: I'm using enums, `EnumSet`s and `ImmutableEnumSet`s in other places, but here it just doesn't feel right. Moreover, in another place I read a list of values from a property-like file. The list is still fixed during the whole program run, but may change between runs. – maaartinus Aug 12 '11 at 06:45
  • @JoachimSauer let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/2394/discussion-between-maaartinus-and-joachim-sauer) – maaartinus Aug 12 '11 at 06:45
  • I requested an enhancement to Guava for this: https://code.google.com/p/guava-libraries/issues/detail?id=1059 – Adam Gent Jul 06 '12 at 12:56
  • There's another use case where an immutable BitSet would be nice to have. It is basically the same as described [here](http://stackoverflow.com/questions/10621176/eclipselink-not-saving-update-to-a-field-inside-an-entity), namely for fields of a JPA entity. At least in EclipseLink, if you modify a mutable field, this is not detected and updated to the database. If you use @Mutable as a fix, you get "Class [...] could not be weaved for change tracking as it is not supported by its mappings.", which I guess could mean that the entity will be regarded as dirty a lot more often (?). – Hein Blöd Apr 14 '15 at 15:39

7 Answers7

6

You could use BigInteger, since it has setBit, testBit and clearBit.

Jeff Foster
  • 43,770
  • 11
  • 86
  • 103
  • This could work, just the conversion to and from `BitSet` (which I want to use as builder) could get complicated. – maaartinus Aug 12 '11 at 06:53
  • The problem with `BigInteger` is that it doesn't allow leading zeroes; the highest bit is always set. – erickson Nov 22 '11 at 20:32
  • @erickson: I don't think so. For each non-empty BitSet there's a highest bit; for the empty BitSet use BigInteger.ZERO. You need no leading zeros: Each number can be viewed as having an infinite number of leading zero and each BitSet has an infinite number of implicit cleared bits -- the situation is the same in both cases. – maaartinus Jul 14 '12 at 21:50
  • @maaartinus Sure, if you feel okay about pulling that logic out of the set and into your client(s), it can obviously be used that way. – erickson Jul 14 '12 at 22:07
3

It's easy to make a practically immutable BitSet from a java.util.BitSet by extending it and knock out modifier methods with throws UnsupportedException or empty block.

However, since BitSet's field which stores effective data isn't final, you have to apply one of the safe publication idioms to achieve thread-safety (copied from here):

  • Initializing an object reference from a static initializer;
  • Storing a reference to it into a volatile field or AtomicReference;
  • Storing a reference to it into a final field of a properly constructed object
  • Storing a reference to it into a field that is properly guarded by a lock.

Another solution could be to make a new ImmutableBitSet class, embed a BitSet into it as a field (with final modifier) and delegate the embedded object's reader methods to the new class.

Note that the latter solution doesn't break Liskow Substitution Principle while the first one does.

Community
  • 1
  • 1
pcjuzer
  • 2,724
  • 4
  • 25
  • 34
  • I tried it, and it's really easy. However, I don't think I achieve thread safety this way - there's nothing final there. – maaartinus Aug 11 '11 at 13:58
  • Hey! If it's immutable, it's inherently thread safe. Go it over.Do you mean 'final' as the keyword? What do you want with it? It has nothing with thread-safety. Maybe you think about 'synchronized'. Anyway, you can put it on the methods of your extension class, if you want. – pcjuzer Aug 15 '11 at 08:35
  • Yes, immutable implies thread-safe, but only because of the [memory barrier at the very end of a constructor](http://stackoverflow.com/questions/2513597/what-is-an-incompletely-constructed-object/2513624#2513624), which I somehow missed to think about. And no, in no case I'm confusing "final" and "synchronized". Sorry, but saying that "final" has nothing to do with "thread-safe" is plain wrong. – maaartinus Aug 15 '11 at 18:44
  • Well, I meant "final" as the keyword which prevents method overriding or extension of class, which afterall has some indirect relation with thread-safety but not much. But I see you think about 'final' which prevents reassigning fields or local variables which, you're right, has more relation with thread-safety. But an object can be 'effectively immutable' even by not having 'final' fields, if the fields practically never change. Knocking out modifiers exactly does this. java.util.Collections.unmodifiableList() does similar thing but not with extension but with delegation. – pcjuzer Aug 16 '11 at 07:12
  • I think, you're wrong. Watch [this talk by Jeremy Manson](http://www.youtube.com/watch?v=1FX4zco0ziY) at 46:10 to see what happens if you omit `final`. – maaartinus Aug 21 '11 at 15:45
  • I will look the whole video because it seems interesting, and maybe I put on another question regarding this. It plainly unbeliveable for me that it's impossible to make thread-safe classes in java by extending mutable classes. – pcjuzer Aug 22 '11 at 07:52
  • I've made a question about this: http://stackoverflow.com/questions/7144915/final-fields-and-thread-safety/7145079#7145079 – pcjuzer Aug 22 '11 at 09:03
  • So it looks like you were wrong with immutability being sufficient for thread-safety, right? In case you agree, please update your answer. – maaartinus Sep 18 '11 at 12:48
  • I agree, edited the answer. I also added one more solution with java.util.BitSet which doesn't break LSP. – pcjuzer Sep 19 '11 at 08:30
3

A workaround:

store the BitSet in a private field and expose it with a cloning public method:

private final BitSet bits;
public BitSet bits(){
    return (BitSet) bits.clone();
}

or:

private final BitSet bits;
public BitSet bits(){
    BitSet clone = new BitSet();
    clone.or(bits);
    return clone;
}
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Nice idea, but... it could get a bit confusing to rely on using a mutable copy for each access. And it also could be used quite inefficiently if you always call `bits()`. – maaartinus Aug 12 '11 at 06:48
  • @martinuus I know. It's not perfect, it's just one of several ideas. Of course if you used it several times per method you'd store it in a local variable first. – Sean Patrick Floyd Aug 12 '11 at 07:44
3

I decided to make a summary of all the answers:

I see no way to get everything perfect, i.e., to get an immutable subclass of BitSet, so that equals works in an thread-safe manner. I admit that I didn't state all my requirements in the question.

Inheriting from BitSet and letting all the mutator methods throw an exception is easy and works. The only problem is that equals called from BitSet itself is not thread-safe since it accesses the non-final inherited fields directly. All other methods can be made thread-safe by a trick described below.

Delegating to BitSet is also easy and works, and its only problem is that a BitSet can't be equal to an ImmutableBitSet. Note that for thread safety the delegate must be stored in a final field.

Combining inheritance and delegation looks promising:

public class ImmutableBitSet extends BitSet {
    private final ImmutableBitSet delegate;

    public ImmutableBitSet(BitSet original) {
        or(original); // copy original to this
        delegate = this; // initialize a final reference for thread safety
    }

    @Override // example mutator method
    public void and(BitSet set) {
        throw new UnsupportedOperationException();
    }

    @Override // example non-mutator method
    public boolean get(int bitIndex) {
        return delegate.getPrivate(bitIndex);
    }

    // needed in order to avoid endless recursion
    private boolean getPrivate(int bitIndex) {
        super.get(bitIndex);
    }

    ...
}

It looks strange, but works nearly perfect. Call to bitSet.equals(immutableBitSet) are not thread-safe, because of them accessing the non-final fields directly. So it was just a fruitless exercise.

Using a BitInteger is quite a lot of work if one wants to implement all the methods and conversion to and from the mutable BitSet. So I'd recommend either delegation or inheritance, depending on the desired behavior of equals and on the need for thread safety.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 2
    Ideally it would be nice to have an ImmutableBitSetBuilder aka Guava style. Anyway I put a feature request into Guava: https://code.google.com/p/guava-libraries/issues/detail?id=1059 . Please vote on it if your interested. – Adam Gent Jul 06 '12 at 12:57
2

You could maybe use BigInteger. It is immutable, and has bit manipulation methods.

Thilo
  • 257,207
  • 101
  • 511
  • 656
2

Personally, I prefer an EnumSet over a BitSet. It is implemented as a bit field, but has the API of a set with strong naming. Really this is the best of both worlds. Guava does provide an ImmutableEnumSet

Ray
  • 4,829
  • 4
  • 28
  • 55
  • I also prefer `EnumSet`, but I don't want to use enums here (as explained in the comments to the question). – maaartinus Aug 12 '11 at 06:51
1

I have implemented such based on org.apache.lucene.util.OpenBitSet from the Apache Lucene project here

http://www.dishevelled.org/bitset

http://www.dishevelled.org/bitset/apidocs/index.html

  • You did, but you doesn't allow `ImmutableBitSet` and `MutableBitSet` be equal. This might be fine, but I wanted to do it the way `List` and `Set` do. – maaartinus Jan 24 '14 at 19:00