Java streams 12. Create stream using class BitSet, JarFile, or Pattern

Nick Samoylov
4 min readFeb 29, 2020

In this post — the last one dedicated to stream creation — we will discuss the following three methods:
IntStream BitSet.stream()
Stream<JarEntry> JarFile.stream()
Stream<String> Pattern.splitAsStream(java.lang.CharSequence)

We demonstrate the usage of each of these methods in the corresponding section below.

IntStream BitSet.stream()

The java.util.BitSet class stores each component of the bit set as a boolean value(0 for false, and 1 for true) in an array. This means that the first bit has the index value 0, while other bits are indexed by positive integers. The size of the BitSet can grow/shrink in increments of 64 as needed. Here is an example:

BitSet bs = new BitSet();
bs.set(63);
System.out.println(bs.size()); //prints: 64
System.out.println(bs.cardinality()); //prints: 1
System.out.println(bs.length()); //prints: 64
bs.set(64);
System.out.println(bs.size()); //prints: 128
System.out.println(bs.cardinality()); //prints: 2
System.out.println(bs.length()); //prints: 65

As you can see, other properties of a BitSet are
— cardinality — number of bits set to true;
— length — “logical size”, equal to the index of the highest set bit plus one.
We are not going to use them. They were demonstrated here just to give you an idea of what BitSet is. The advantage of a BitSet is that it allows performing bitwise logical operations. For example:

bs = new BitSet();
bs.set(9);
bs.set(3);
bs.set(9);
bs.set(7);
System.out.println(bs); //prints: {3, 7, 9}
bs.stream().forEach(System.out::print); //prints: 379

The advantage of a BitSet is that it allows performing bitwise logical operations. For example, let us create a method that displays the content of the first 4 bits:

private static void printFourBits(BitSet bs) {
String b0 = bs.get(0) ? "1" : "0";
String b1 = bs.get(1) ? "1" : "0";
String b2 = bs.get(2) ? "1" : "0";
String b3 = bs.get(3) ? "1" : "0";
System.out.println(b0 + b1 + b2 + b3);
}

We now use it to demonstrate the AND and OR bitwise operations on two BitSet objects. The result of each operation is stored in the first operand. If the second operand has more bits than the first one, those extra bits are ignored. Here is the demo code:

BitSet bs1 = new BitSet();
bs1.set(1);
bs1.set(3);
printBitSetBits(bs1); //prints: 0101
BitSet bs2 = new BitSet();
bs2.set(3);
printBitSetBits(bs2); //prints: 0001
bs1.and(bs2); //bitwise AND
printBitSetBits(bs1); //prints: 0001
bs1 = new BitSet();
bs1.set(1);
bs1.set(3);
printBitSetBits(bs1); //prints: 0101
bs2 = new BitSet();
bs2.set(3);
printBitSetBits(bs2); //prints: 0001
bs1.or(bs2); //bitwise OR
printBitSetBits(bs1); //prints: 0101

Now let us generate a stream from a BitSet object:

bs = new BitSet();
bs.set(9);
bs.set(3);
bs.set(9);
bs.set(7);
System.out.println(bs); //prints: {3, 7, 9}
bs.stream().forEach(System.out::print); //prints: 379

Please, note how setting the number twice does not change the BitSet content. That is because the second setting sets to true the same bit in the BitSet. Also, observe that the stream of the values is always ordered because it receives values in ascending order from the lowest index to the higher ones.

Stream<JarEntry> JarFile.stream()

The JarFile class reads the content of a .jar file. The stream based on the JarFile objects emits an ordered sequence the .jar file entries. For example, we copied the .jar file of this blog project from the “target” directory into the “resources” folder and were able to stream its content as follows:

Path path = Path.of("src", "main", "resources", "javablog-1.0.jar");
File file = new File(path.toString());
try (JarFile jf = new JarFile(file)){
jf.stream().forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}

As you can see, we used try-with-resources construct to close the opened file and to catch all the exceptions. The result is as follows (we show only first few lines of the output):

META-INF/
META-INF/MANIFEST.MF
com/
com/nick/
com/nick/blog/
com/nick/blog/concepts/
com/nick/blog/features/
com/nick/blog/features/streams/
com/nick/blog/features/streams/java8/
...

For a .jar file with multiple releases support, one can use the versionedStream() method which returns a stream that emits the latest versioned entry associated with the corresponding base entry name.

Stream<String> Pattern.splitAsStream(CharSequence)

The splitAsStream() method takes as an input a string and splits it into subsequences. The subsequences separators are described by the regular expression used to create the Pattern object. For example:

Pattern pattern = Pattern.compile("[\\,\\.\\-]");pattern.splitAsStream("one-two,three.four")
.forEach(System.out::println);

As you can see, we have used a regular expression that describes a separator the symbols dot, comma, and dash. The result of the above code is as follows:

Any subsequence can be described in the regular expression and thus used as a separator. This concludes the series of posts about stream creation. In the next post, we will talk about stream operations.

See other posts on Java 8 streams too.

--

--

Nick Samoylov

Born in Moscow, lived in Crimea, now lives in the US. Used to be physicist and rock climber, now programmer and writer.