为啥 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】:
这是因为您的代码将索引视为double
s 数组中的索引。初始写入成功完成;然后第二个写入除第一个之外的字节;第三次写入再次覆盖字节,依此类推。最后一次写入的结果不会被覆盖,所以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中分配字节而不在FloatBuffer中分配浮点数
为啥 ByteBuffer putShort(value) 的方法在我的情况下无法正常工作?