为啥 ByteBuffer 不保留 putDouble 和 getDouble 的索引?

Posted

技术标签:

【中文标题】为啥 ByteBuffer 不保留 putDouble 和 getDouble 的索引?【英文标题】:Why doesn't ByteBuffer preserve index for putDouble and getDouble?为什么 ByteBuffer 不保留 putDouble 和 getDouble 的索引? 【发布时间】:2018-02-27 18:21:47 【问题描述】:

在以下情况下,我很难理解 ByteBuffer 的语义:

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

我希望看到我刚刚放入 ByteBuffer 的值打印到屏幕上。相反,我得到了这个输出:

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

第三个索引的值似乎符合我的预期:4.0。但是为什么值和索引 0、1 和 2 与我插入的值(分别为 1.0、2.0 和 3.0)不匹配?

我怀疑我误解了 ByteBuffer 的工作原理,但我无法在 javadoc 中找到它。

【问题讨论】:

我还没有与ByteBuffer 合作过,但是您不必访问8 的倍数的索引吗?毕竟,double 有 8 个字节大。例如:testBuf.putDouble(0, 1.0); testBuf.putDouble(8, 2.0); testBuf.putDouble(16, 3.0); testBuf.putDouble(24, 4.0);getDouble 也是如此。 【参考方案1】:

这是因为您的代码将索引视为doubles 数组中的索引。初始写入成功完成;然后第二个写入除第一个之外的字节;第三次写入再次覆盖字节,依此类推。最后一次写入的结果不会被覆盖,所以4.0 完好无损。

第一个参数表示缓冲区内字节数组的索引,因此您需要将其乘以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.

【讨论】:

谢谢!这很有意义。而且我得到的最后一个索引的“正确”值也是有道理的。漂亮! 当然,由于您只使用双精度数,因此您可以只使用 DoubleBuffer,因此索引是双精度数,而不是字节数。【参考方案2】:

putDouble() 的第一个参数是一个字节的索引,而不是一个双精度的索引。所以你放的双打是重叠的;第四个是最后写入的,所以它被保留了,但是之前的已经损坏了。

要得到你想要的,你需要在写和读时将索引乘以Double.BYTES

以下按预期工作:

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

【讨论】:

或者不是乘以 8,而是使用 Double.BYTES 来获取平台上的字节。 (可能是 8,但为什么要引入新常量?) 谢谢!我只能为这个问题选择一个答案,但你的解释也是正确的。感谢您的帮助。 @markspace 感谢您注意到这一点,我已经更新了答案。【参考方案3】:

由于您只使用 double 值,您可以使用 DoubleBuffer 代替:

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

或者你可以用DoubleBuffer 包裹ByteBuffer

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

输出

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

【讨论】:

以上是关于为啥 ByteBuffer 不保留 putDouble 和 getDouble 的索引?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ByteBuffer.allocate() 和 ByteBuffer.allocateDirect() 之间的奇怪性能曲线差异

为啥我不能将字符串添加到 ByteBuffer?

为啥我们可以直接在ByteBuffer中分配字节而不在FloatBuffer中分配浮点数

为啥 ByteBuffer putShort(value) 的方法在我的情况下无法正常工作?

为啥 localStorage 不保留在 Chrome 中?

为啥在启用 ARC 的项目中不需要维护保留计数