4

I have a library that uses BitSet, and I need to change Set[Int] type data to use the library.

What comes to mind is to use .toSeq:_* operations, but I'm not sure this is efficient way to convert from BitSet to Set[Int] or the other way round.

scala> BitSet(Set(1,2,3).toSeq:_*)
res55: scala.collection.immutable.BitSet = BitSet(1, 2, 3)

scala> Set(BitSet(1,2,3).toSeq:_*)
res56: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

Is there a better way?

prosseek
  • 182,215
  • 215
  • 566
  • 871

2 Answers2

5

Scala's Set hierarchy is a little weird, but it is the case that BitSet has Set[Int] as a super type, so you can simply pass a BitSet to a method that expects a Set[Int], etc.

This might not seem to be the case because the Set you get by default is immutable.Set (under scala.collection), while it's possible that the library you're using is working with the BitSet directly under scala.collection, not immutable.BitSet. This isn't the case in your example code, where everything is in immutable, but I'm not sure how simplified that is.

If you're lucky enough to be working with the immutable package's versions of both Set and BitSet, the BitSet to Set direction is trivial:

scala> import scala.collection.immutable.BitSet
import scala.collection.immutable.BitSet

scala> val bs = BitSet(0, 100, 200)
bs: scala.collection.immutable.BitSet = BitSet(0, 100, 200)

scala> def takesSet(s: Set[Int]): Int = s.size
takesSet: (s: Set[Int])Int

scala> takesSet(bs)
res0: Int = 3

If somehow you've got a scala.collection.BitSet, just use toSet:

scala> takesSet(scala.collection.BitSet(0, 100, 200).toSet)
res1: Int = 3

For the other direction, your version is fine:

scala> val s = Set(1, 2, 3)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> BitSet(s.toSeq: _*)
res2: scala.collection.immutable.BitSet = BitSet(1, 2, 3)

But it's also worth noting that in many cases you can avoid this conversion with some CanBuildFrom magic:

scala> val bs: BitSet = Set("1", "2", "3").map(_.toInt)(collection.breakOut)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)

This produces the same result as the following:

scala> val bs: BitSet = BitSet(Set("1", "2", "3").map(_.toInt).toSeq: _*)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)

But instead of constructing an intermediate set (and sequence) before the BitSet, the collection.breakOut parameter tells the compiler to perform the map using an instance of the CanBuildFrom type class that will construct the BitSet directly. Passing a CanBuildFrom instance explicitly in this way isn't just for map, either—it'll work with flatMap, scanLeft, ++, and other collections operations that allow you to change the element type.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
0

Think object-oriented: BitSet is a Set[Int], you don't need to do anything to convert one to the other:

     import scala.collection._
     val set: Set[Int] = BitSet(1,2,3)

Going the other way is harder, and will take linear time:

     val bs: BitSet = BitSet(set.toStream:_*)

but whenever you find yourself needing this kind of "upconverting", 99% of the time it is a sign of bad design, than should be improved to eliminate the need: the implementation details of the particular Set should only matter to the code that creates it initially, downstream code should usually be agnostic of the implementation.

Dima
  • 39,570
  • 6
  • 44
  • 70
  • I think that "99%" is hugely optimistic. It'll often be the case that you need to work with `BitSet`s (either because of a third-party API or because you need `BitSet`-specific operations), and it'll often be the case that you need to "lower" ordinary sets into the `BitSet` subtype to make that possible. – Travis Brown Apr 02 '16 at 19:48
  • 1
    @TravisBrown maybe, I am an optimist :) I am just finding it hard to think of a use case, where I would need a bit set, that _I have not created_. I am sure there are some, just saying I think they are nearly not as often as when people just do these conversions back-and-forth without thinking about whether they really need it. The fact, that `Set` you get from `Predef` is `scala.collection.immutable.Set` and not `scala.collection.Set` also doesn't help IMHO. – Dima Apr 02 '16 at 19:57