1

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

7
  • 1
    Are you trying to convert each double to a single byte, or to the 8 bytes that make it up? Commented May 20, 2018 at 14:21
  • 1
    There is no ByteStream hence the map operation will yield a DoubleStream and therefore you're getting back a double[] when you call toArray() Commented May 20, 2018 at 14:28
  • 1
    "or I'm goin about this all wrong in general" You are. Don't use streams. A normal for loop of copying values from double[] to byte[] would do it, and run much faster too. Commented May 20, 2018 at 14:31
  • "I don't understand the map method" The map method of DoubleStream maps to another DoubleStream. You can use mapToInt​, mapToLong, or mapToObj if you want a different type of stream, but there is no mapToByte. Commented May 20, 2018 at 14:35
  • 1
    In any case, don't use Double.valueOf(d).byteValue(): simply use (byte) d, to avoid the unnecessary boxing. Commented May 20, 2018 at 14:39

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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();

1 Comment

For some reason I was trying to avoid this, but thanks. I guess this is the best answer
0

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;
}

Comments

0

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[].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.