3

I want an InputStream to a sequence of bytes: 0, 1, 2, ... 255.

I can of course create a new byte[0x100], create a loop of int, fill it with the int values cast to byte (don't get me started on Java's signed byte type), and then form a ByteArrayInputStream from that.

But surely with Java 8 there is a better, more compact, and cleverer way. The trick seems to be generating the array of bytes. I found elsewhere that with int it's as easy as the following:

final int[] values =  IntStream.range(0, 0x100).toArray();

But I need a byte array, and there is no ByteStream. Perhaps there is an IntStream collection function that could collect the int values into a byte[] array? Or something even cleverer?

Community
  • 1
  • 1
Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • Can you clarify whether you're starting from an InputStream or a byte sequence? Your question suggests both. And what form does the source or desired byte sequence take? E.g., is it a byte[], or a Stream, or something else? – Andy Thomas Apr 27 '16 at 20:46
  • An `InputStream` that produces a particular sequence is where I want to _end_, not start. A `byte[]` seems an obvious means, but feel free to skip the byte array if you have a more direct trick for creating an `InputStream`. – Garret Wilson Apr 27 '16 at 21:32
  • What form does your byte sequence take? Is it the specific sequence {1,2,...,255}? Or is it some type of container of arbitrary bytes? – Andy Thomas Apr 27 '16 at 22:08
  • Let's go with `0` – `255` for the sake of argument. – Garret Wilson Apr 27 '16 at 22:51

4 Answers4

1

You could declare the byte[] and then use an IntStream with a forEach to fill it. Something like

byte[] arr = new byte[256];
IntStream.range(0, arr.length).forEach(x -> arr[x] = (byte) x);
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
0

A loop would be faster, but if you are asking for the sake of discussion how you might solve it with streams, you could do this, with a little help from the guava Bytes api:

byte[] bytes = IntStream.range(0, 0x100)
    .mapToObj(i -> (byte) i)                        // boxes into a Byte
    .collect(Collectors.collectingAndThen(Collectors.toList(), Bytes::toArray))

In this example, each int is wrapped in a Byte, which are collected to a List and then converted to a byte[].

The advantage of this approach is that it works without knowing the size of the destination array in advance. If you know the size of the array, @ElliotFrisch's answer would be more efficient, and a loop more efficient still.

Hank D
  • 6,271
  • 2
  • 26
  • 35
0

It's too bad that there isn't a byte version of Arrays.setAll. It would have worked like this:

byte[] foo = new byte[0x100];
setAll(foo, i -> i++);

If someone really wanted it though, they could use this, adapted from the int version of Arrays.setAll:

public static void setAll(byte[] array, IntUnaryOperator generator) {
    Objects.requireNonNull(generator);
    for (int i = 0; i < array.length; i++)
        array[i] = (byte)generator.applyAsInt(i);
}
Hank D
  • 6,271
  • 2
  • 26
  • 35
0

If you want to create lazy InputStream from an IntStream (using least significant bytes only), you may try this:

public static InputStream asByteInputStream(IntStream is) {
    Spliterator.OfInt spltr = is.spliterator();
    return new InputStream() {
        private int last;

        @Override
        public int read() {
            return spltr.tryAdvance((int val) -> last = val) ? (last & 0xFF) : -1;
        }

        @Override
        public void close() {
            is.close();
        }
    };
}

Usage tests:

AtomicBoolean flag = new AtomicBoolean(false);
InputStream is = asByteInputStream(IntStream.range(0, 256).onClose(() -> flag.set(true)));
byte[] data = new byte[256];
assertEquals(2, is.skip(2));
// read 3..255 into data[0..253]
assertEquals(254, is.read(data));
// nothing more in the input
assertEquals(-1, is.read());
for(int i=0; i<254; i++) {
    assertEquals((byte)(i+2), data[i]);
}
assertEquals(0, data[254]);
assertEquals(0, data[255]);

// close is forwarded to original IntStream
assertFalse(flag.get());
is.close();
assertTrue(flag.get());
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334