Typhaon
Typhaon

Reputation: 1054

Casting double[] to byte[] using stream in Java

Hey I'm working on an app that uses Paho mqtt

Now I'm trying to cast the contents of a couple of objects to byte arrays so I can send them to the broker. There are a couple of different objects that all adhere to a abstract class, but the one I started with contains a double[]

Here's the function I'm trying to implement:

    @Override
public byte[] getBytes() {

    return Arrays.stream(driveVector).map(d -> Double.valueOf(d).byteValue()).toArray();
}

I thought this would work, but I get an error that the return value is a double[]

I think I either don't understand the map method or I'm goin about this all wrong in general (I looked at the ByteBuffer class, but it seems like a pain to implement this with it)

Thanks in advance

Upvotes: 1

Views: 528

Answers (4)

Andy Turner
Andy Turner

Reputation: 140318

You can't cast a double[] to a byte[] for the fundamental reason that they are unrelated types, and you can only cast between related types.

Casts in Java, unlike, say, C++, don't actually create a new object: they are merely a way to the compiler "I know more about the type of this object than you; trust me." For example, you might know that a variable of type Object actually holds a reference to a String, something which the compiler cannot know; in that case, you can cast the reference.

You can, however, construct a new array:

byte[] output = new byte[input.length];
for (int j = 0; j < input.length; j++) {
  output[j] = (byte) input[j];
}

There is no way to do this with streams. Or rather, there is, in that you could crowbar this code into a stream operation on a Stream<double[]>, say; but involving streams like that clearly adds no benefit.

Upvotes: 2

M. le Rutte
M. le Rutte

Reputation: 3563

In my MQTT application I read a single double value and post that to the broker. However, there is no real difference between a single and an array of doubles. The client needs to know the array length, while with a single value it always knows there is one.

I'm confident that you can adapt my code to writing multiple values, adapt the toMessage to write multiple double values.

public abstract class SensorMonitor {
  protected final MqttAsyncClient client;
  protected final String topic;
  protected final Logger logger = Logger.getLogger(getClass().getName()); 
  private final ByteArrayOutputStream byteOut = new ByteArrayOutputStream(8);
  private final DataOutputStream dataOut = new DataOutputStream(byteOut);

  public SensorMonitor(MqttAsyncClient mqttClient, String topic) {
    this.client = mqttClient;
    this.topic = topic;
  }

  public void start(ScheduledExecutorService service) {
    service.scheduleWithFixedDelay(this::publish, 0, 30, TimeUnit.SECONDS);
  }

  protected void publish() {
    try {
        MqttMessage message = toMessage(readNewValue());
        client.publish(topic, message);

    } catch (MqttException | IOException e) {
        logger.log(Level.SEVERE, "Could not publish message", e);
    }
  }

  private MqttMessage toMessage(double value) throws IOException {
    byteOut.reset();
    dataOut.writeDouble(value);
    return new MqttMessage(byteOut.toByteArray());
  }

  protected abstract double readNewValue();
}

The DataOutputStream.writeDouble uses Double.doubleToLongBits to create a IEEE 754 floating-point "double format" bit layout.

In my case I could pre-alloc and reuse the byteOut output stream as I knew upfront the needed size of the byte[].

Upvotes: 0

Nikita Ryanov
Nikita Ryanov

Reputation: 1570

You can use ByteBuffer for it:

double[] doubles = new double[] {1,2,3,4,5};
ByteBuffer buffer = ByteBuffer.allocate(doubles.length * Double.BYTES);
Arrays.stream(doubles).forEach(buffer::putDouble);
buffer.array();

Upvotes: 1

Andreas
Andreas

Reputation: 159106

Java Streams is not the right tool here, especially not since there is no ByteStream in Java.

Your method can be implemented as a simple for loop.

@Override
public byte[] getBytes() {
    byte[] arr = new byte[driveVector.length];
    for (int i = 0; i < arr.length; i++)
        arr[i] = (byte) driveVector[i];
    return arr;
}

Upvotes: 0

Related Questions