为何ByteBuffer的putDouble和getDouble不保留索引?取值异常求助
为什么ByteBuffer的putDouble和getDouble方法看起来“不保留索引”?
哈哈,这个坑我刚学Java NIO的时候也踩过!其实核心问题是你误解了ByteBuffer里「索引」的含义,再加上double类型的存储特性,才导致了预期和实际结果不匹配的情况。
先搞懂两个关键知识点
1. double类型的字节占用
在Java里,一个double类型的值要占8个字节的存储空间。也就是说,你每往ByteBuffer里存一个double,它都会连续占用8个字节的位置,而不是1个。
2. ByteBuffer的索引是「字节索引」,不是「元素索引」
ByteBuffer里的index参数,指的是单个字节的位置,不是你存入的每个double的“第N个元素”位置。比如:
- 当你调用
putDouble(1.0)时,它会从当前position开始,把1.0的8个字节写入索引0-7的位置,然后position自动跳到8; - 接着调用
putDouble(2.0),会写入索引8-15的位置,position跳到16; - 以此类推,第三个double存在16-23,第四个存在24-31。
你的问题出在哪?
你以为调用getDouble(0)能拿到1.0,getDouble(1)能拿到2.0,但实际上:
getDouble(1)是从字节索引1开始,连续读8个字节(1-8),这部分数据是1.0的后7个字节+2.0的第1个字节,完全不是一个完整的double值,解析出来自然是乱码;- 同理
getDouble(2)读的是字节2-9,也是错位的字节组合,结果肯定不对; - 而你说“第三个索引处的值符合预期(4.0)”,大概率是你误打误撞用了
getDouble(24)(第四个double的起始字节索引),刚好命中了完整的4.0的字节数据。
正确的用法应该是怎样的?
根据你的需求,有两种靠谱的方式:
方式1:顺序写入+顺序读取
如果是按顺序存数据,用不带索引的putDouble(),读取时先调用flip()切换到读模式,再用不带索引的getDouble()顺序读取:
ByteBuffer buf = ByteBuffer.allocate(32); // 顺序写入四个double buf.putDouble(1.0); buf.putDouble(2.0); buf.putDouble(3.0); buf.putDouble(4.0); // 切换到读模式(把position重置为0,limit设为原来的position) buf.flip(); // 顺序读取,依次输出1.0、2.0、3.0、4.0 while (buf.hasRemaining()) { System.out.println(buf.getDouble()); }
方式2:按「元素索引」存取(手动计算字节位置)
如果你想按“第N个double”来指定位置,就需要把元素索引乘以8,得到对应的字节起始索引:
ByteBuffer buf = ByteBuffer.allocate(32); // 按元素索引写入:第0个、第1个、第2个、第3个double buf.putDouble(0 * 8, 1.0); buf.putDouble(1 * 8, 2.0); buf.putDouble(2 * 8, 3.0); buf.putDouble(3 * 8, 4.0); // 按元素索引读取,结果完全符合预期 System.out.println(buf.getDouble(0 * 8)); // 1.0 System.out.println(buf.getDouble(1 * 8)); // 2.0 System.out.println(buf.getDouble(2 * 8)); // 3.0 System.out.println(buf.getDouble(3 * 8)); // 4.0
总结一下
ByteBuffer的索引从来不是“保留”或者“不保留”的问题,而是你得明白它是字节级别的位置标识。对于double这种占多字节的类型,必须保证存取的起始索引是8的倍数,才能读取到完整、正确的数值。
内容的提问来源于stack exchange,提问作者Kirby
相关产品推荐
相关产品推荐

