6
\$\begingroup\$

I'm just returning to Java after a multi-decade hiatus -- there are some nice new packages, such as java.nio.

I need to convert an array of floats into bytes and vice versa. The floatToBytes() function feels okay, but I'm pretty certain that bytesToFloats() could be implemented better. Thoughts?

(P.S.: ignore the public, protected etc -- this code is wrapped inside a class...)

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

    protected static final int BYTES_PER_FLOAT = Float.SIZE / 8;

    public static byte[] floatsToBytes(float[] floats){
        ByteBuffer buffer = 
                ByteBuffer.allocate(BYTES_PER_FLOAT * floats.length).
                order(ByteOrder.BIG_ENDIAN);
        for (float f : floats) {
            buffer.putFloat(f);
        }
        return buffer.array();
    }
    
    protected static float[] bytesToFloats(byte[] bytes) {
        if (bytes.length % BYTES_PER_FLOAT != 0) {
            throw new RuntimeException("Illegal length");
        }
        ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
        int n_floats = bytes.length / BYTES_PER_FLOAT;
        float[] floats = new float[n_floats];
        for (int i=0; i<n_floats; i++) {
            floats[i] =  buffer.getFloat(i * BYTES_PER_FLOAT);
        }
        return floats;
    }

I also note that there's a FloatBuffer class -- would that be appropriate here?

\$\endgroup\$
4
\$\begingroup\$

Your code is way more verbose than it needs to be.

First of all, BYTES_PER_FLOAT is unnecessary. You could simply use Float.BYTES, a built-in constant.

Secondly, the initial ordering of a ByteBuffer is always BIG_ENDIAN, so you don't need to set this explicitly.

Finally, as dariosicily indicated, using asFloatBuffer() allows bulk operations, but additionally, you should "Invocation chaining" to make the code significantly shorter:

class Convert {

    public static byte[] floatsToBytes(float[] floats) {
        byte bytes[] = new byte[Float.BYTES * floats.length];
        ByteBuffer.wrap(bytes).asFloatBuffer().put(floats);
        return bytes;
    }
    
    public static float[] bytesToFloats(byte[] bytes) {
        if (bytes.length % Float.BYTES != 0)
            throw new RuntimeException("Illegal length");
        float floats[] = new float[bytes.length / Float.BYTES];
        ByteBuffer.wrap(bytes).asFloatBuffer().get(floats);
        return floats;
    }
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ More compact code than my answer and I completely missed the reference to the implicit order , from documentation seems that Float.BYTES constant has been introduced from java 8 version while it does not exist in java previous versions. \$\endgroup\$ Jul 31 '20 at 5:48
  • \$\begingroup\$ Perfect use of chaining -- thank you. In this specific case, it's important that the future developer (or "future me") know the byte ordering, so I inserted .order(ByteOrder.BIG_ENDIAN) into the chain. \$\endgroup\$ Jul 31 '20 at 12:24
4
\$\begingroup\$

Welcome again to Code Review, not my field but from a not so clear documentation it seems that you can rewrite your bytesToFloats method using the FloatBuffer class like below, using the asFloatBuffer method from ByteBuffer class:

ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
FloatBuffer fb = buffer.asFloatBuffer();

After you have to back the FloatBuffer buffer allocating a new float array like below:

float[] floats = new float[bytes.length / BYTES_PER_FLOAT];
fb.get(floats);

Then your method can be rewritten like below:

protected static float[] bytesToFloats(byte[] bytes) {

    if (bytes.length % BYTES_PER_FLOAT != 0) {
        throw new RuntimeException("Illegal length");
    }
    
    ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
    FloatBuffer fb = buffer.asFloatBuffer();
    float[] floats = new float[bytes.length / BYTES_PER_FLOAT]; 
    
    fb.get(floats);

    return floats;

}

In alternative to the bytes.length / BYTES_PER_FLOAT the fb.limit() method seems returning the same result, moreover it is bound directly to the FloatBuffer fb used.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.