4

I'm having difficulty understanding the semantics of ByteBuffer in the following scenario:

int sizeOfDouble = 8;
int numberOfDoubles = 4;
ByteBuffer testBuf = ByteBuffer.allocateDirect(sizeOfDouble*numberOfDoubles);

testBuf.putDouble(0, 1.0);
testBuf.putDouble(1, 2.0);
testBuf.putDouble(2, 3.0);
testBuf.putDouble(3, 4.0);

for (int i = 0; i < numberOfDoubles; ++i) {
    System.out.println("testBuf[" + i + "]: " + testBuf.getDouble(i));
}

I expected to see the values I had just put into the ByteBuffer to be printed to the screen. Instead, I get this output:

testBuf[0]: 4.959404759574682E-4
testBuf[1]: 32.50048828125
testBuf[2]: 32.125
testBuf[3]: 4.0

The value at the third index seems to match what I expect: 4.0. But why aren't the values and indices 0, 1, and 2 matching up with the values I inserted (1.0, 2.0, and 3.0, respectively)?

I suspect I misunderstand something about how ByteBuffer works, but I haven't been able to find it in the javadoc.

Kirby
  • 3,649
  • 3
  • 26
  • 28
  • I haven't worked with `ByteBuffer` yet, but don't you have to access the indices in multiples of 8? After all, a `double` is 8 bytes big. E.g.: `testBuf.putDouble(0, 1.0); testBuf.putDouble(8, 2.0); testBuf.putDouble(16, 3.0); testBuf.putDouble(24, 4.0);`. The same goes for `getDouble` then. – QBrute Feb 27 '18 at 18:27

3 Answers3

5

This is because your code treats index as an index in an array of doubles. The initial write completes successfully; then the second one writes over its bytes except the first one; the third write overwrites the bytes again, and so on. The results of the last write don't get written over, so 4.0 comes out intact.

The first argument represents an index into the array of bytes inside the buffer, so you need to multiply it by sizeOfDouble:

testBuf.putDouble(0*sizeOfDouble, 1.0);
testBuf.putDouble(1*sizeOfDouble, 2.0);
testBuf.putDouble(2*sizeOfDouble, 3.0);
testBuf.putDouble(3*sizeOfDouble, 4.0);

for (int i = 0; i < numberOfDoubles; ++i) {
    System.out.println("testBuf[" + i + "]: " + testBuf.getDouble(sizeOfDouble*i));
}

Demo.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Thanks! That makes perfect sense. And it also makes sense that I was getting the "correct" value for the last index I added. Beautiful! – Kirby Feb 27 '18 at 18:31
  • Of course, since you're only working with doubles, you could just use a [`DoubleBuffer`](https://docs.oracle.com/javase/8/docs/api/java/nio/DoubleBuffer.html) instead, so the index is in doubles, not bytes. – Andreas Feb 27 '18 at 18:37
4

The first argument of putDouble() is an index of a byte and not an index of a double. So the doubles you put are overlapping; the fourth is written last, so it is preserved, but the previous ones are corrupted.

To get what you want, you need to multiply the index by Double.BYTES both when you write and read them.

The following works as expected:

int numberOfDoubles = 4;
ByteBuffer testBuf = ByteBuffer.allocateDirect(Double.BYTES*numberOfDoubles);

testBuf.putDouble(0, 1.0);
testBuf.putDouble(Double.BYTES, 2.0);
testBuf.putDouble(Double.BYTES * 2, 3.0);
testBuf.putDouble(Double.BYTES * 3, 4.0);

for (int i = 0; i < numberOfDoubles; ++i) {
    System.out.println("testBuf[" + i + "]: " + testBuf.getDouble(i * Double.BYTES));
}
Roman Puchkovskiy
  • 11,415
  • 5
  • 36
  • 72
1

Since you're only working with double values, you could use a DoubleBuffer instead:

int numberOfDoubles = 4;
DoubleBuffer testBuf = DoubleBuffer.allocate(numberOfDoubles);

testBuf.put(0, 1.0);
testBuf.put(1, 2.0);
testBuf.put(2, 3.0);
testBuf.put(3, 4.0);

for (int i = 0; i < numberOfDoubles; ++i) {
    System.out.println("testBuf[" + i + "]: " + testBuf.get(i));
}

Or you could wrap the ByteBuffer with a DoubleBuffer:

int sizeOfDouble = Double.BYTES;
int numberOfDoubles = 4;
ByteBuffer testBuf = ByteBuffer.allocateDirect(sizeOfDouble*numberOfDoubles);
DoubleBuffer dblBuf = testBuf.asDoubleBuffer();

dblBuf.put(0, 1.0);
dblBuf.put(1, 2.0);
dblBuf.put(2, 3.0);
dblBuf.put(3, 4.0);

for (int i = 0; i < numberOfDoubles; ++i) {
    System.out.println("dblBuf[" + i + "]: " + dblBuf.get(i));
}
for (int i = 0; i < testBuf.limit(); ++i) {
    System.out.println("testBuf[" + i + "]: " + testBuf.get(i));
}

Output

dblBuf[0]: 1.0
dblBuf[1]: 2.0
dblBuf[2]: 3.0
dblBuf[3]: 4.0
testBuf[0]: 63
testBuf[1]: -16
testBuf[2]: 0
testBuf[3]: 0
testBuf[4]: 0
testBuf[5]: 0
testBuf[6]: 0
testBuf[7]: 0
testBuf[8]: 64
testBuf[9]: 0
testBuf[10]: 0
testBuf[11]: 0
testBuf[12]: 0
testBuf[13]: 0
testBuf[14]: 0
testBuf[15]: 0
testBuf[16]: 64
testBuf[17]: 8
testBuf[18]: 0
testBuf[19]: 0
testBuf[20]: 0
testBuf[21]: 0
testBuf[22]: 0
testBuf[23]: 0
testBuf[24]: 64
testBuf[25]: 16
testBuf[26]: 0
testBuf[27]: 0
testBuf[28]: 0
testBuf[29]: 0
testBuf[30]: 0
testBuf[31]: 0
Andreas
  • 154,647
  • 11
  • 152
  • 247