Java 如何避免制作ByteBuffer的防御性副本?

Java 如何避免制作ByteBuffer的防御性副本?,java,immutability,nio,Java,Immutability,Nio,我有一个类,它接受ByteBuffer作为构造函数参数。有没有一种方法可以避免制作防御性拷贝,以确保缓冲区不会被修改超过该点 isReadOnly()不保证原始所有者不会修改缓冲区。更糟糕的是,似乎没有办法将ByteBuffer子类化。有什么想法吗?唯一真正的方法是,正如您所说的,buf.asReadOnlyBuffer(),然后将其传递给构造函数。除此之外,没有其他选项,尽管您可以将内容复制到新的ByteBuffer,然后将其传递。不会避免复制,但可能: 使用预填充的预分配ByteBuffer

我有一个类,它接受ByteBuffer作为构造函数参数。有没有一种方法可以避免制作防御性拷贝,以确保缓冲区不会被修改超过该点


isReadOnly()不保证原始所有者不会修改缓冲区。更糟糕的是,似乎没有办法将ByteBuffer子类化。有什么想法吗?

唯一真正的方法是,正如您所说的,
buf.asReadOnlyBuffer()
,然后将其传递给构造函数。除此之外,没有其他选项,尽管您可以将内容复制到新的
ByteBuffer
,然后将其传递。

不会避免复制,但可能:

  • 使用预填充的预分配ByteBuffers池
  • 允许 作者类的构造函数,以允许传入 ByteBuffer,但让类使用池中的ByteBuffer进行移动 应用程序启动/关闭的分配/解除分配成本。这样只需支付memcopy费用

  • 这并不能完全回答这个问题,但是对于某些用法(例如,如果您主要尝试强制执行“按合同设计”),它可能已经足够好了,而且效率更高。对于其他用途,它将不起作用,效率可能会低得多

    在构造过程中,保存ByteBuffer的哈希代码

    final int originalBBHashCode=byteBuffer.hashCode()


    然后,在代码中要验证ByteBuffer没有更改的几个关键位置,验证ByteBuffer.hashCode()==originalBBHashCode。如果不是,抛出一个异常。坦白地说,我很想抛出ConcurrentModificationException,因为这是您正在模仿的行为,但YMMV。

    这是我目前能做的最好的:

    /**
     * Helper functions for java.nio.Buffer.
     * <p/>
     * @author Gili Tzabari
     */
    public final class Buffers
    {
        /**
         * Returns a ByteBuffer that is identical but distinct from the original buffer.
         * <p/>
         * @param original the buffer to copy
         * @return an independent copy of original
         * @throws NullPointerException if original is null
         */
        public static ByteBuffer clone(ByteBuffer original)
        {
            Preconditions.checkNotNull(original, "original may not be null");
    
            ByteBuffer result = ByteBuffer.allocate(original.capacity());
            ByteBuffer source = original.duplicate();
            source.rewind();
            result.put(source);
    
            try
            {
                source.reset();
                result.position(source.position());
                result.mark();
            }
            catch (InvalidMarkException unused)
            {
                // Mark is unset, ignore.
            }
            result.position(original.position());
            result.limit(original.limit());
            return result;
        }
    
        /**
         * Returns an array representation of a buffer. The returned buffer may, or may not, be tied to
         * the underlying buffer's contents (so it should not be modified).
         * <p/>
         * @param buffer the buffer
         * @return the remaining bytes
         */
        public static byte[] toArray(ByteBuffer buffer)
        {
            if (buffer.hasArray() && !buffer.isReadOnly() && buffer.position() == 0
                && buffer.remaining() == buffer.limit())
            {
                return buffer.array();
            }
            ByteBuffer copy = buffer.duplicate();
            byte[] result = new byte[copy.remaining()];
            copy.get(result);
            return result;
        }
    
        /**
         * Prevent construction.
         */
        private Buffers()
        {
        }
    }
    
    /**
    *java.nio.Buffer的助手函数。
    *

    *@作者Gili Tzabari */ 公共最终类缓冲区 { /** *返回与原始缓冲区相同但不同的ByteBuffer。 *

    *@param-original要复制的缓冲区 *@返回原件的独立副本 *@original为null时抛出NullPointerException */ 公共静态ByteBuffer克隆(ByteBuffer原始) { 先决条件。checkNotNull(原件,“原件不得为空”); ByteBuffer结果=ByteBuffer.allocate(original.capacity()); ByteBuffer source=original.duplicate(); source.rewind(); 结果。put(来源); 尝试 { source.reset(); result.position(source.position()); result.mark(); } 捕获(InvalidMarkException未使用) { //标记未设置,忽略。 } result.position(原始.position()); result.limit(原始.limit()); 返回结果; } /** *返回缓冲区的数组表示形式。返回的缓冲区可以绑定到,也可以不绑定到 *基础缓冲区的内容(因此不应修改)。 *

    *@param buffer缓冲区 *@返回剩余的字节 */ 公共静态字节[]toArray(字节缓冲区) { if(buffer.hasArray()&&&!buffer.isReadOnly()&&buffer.position()==0 &&buffer.remaining()==buffer.limit()) { 返回buffer.array(); } ByteBuffer copy=buffer.duplicate(); 字节[]结果=新字节[copy.remaining()]; 复制。获取(结果); 返回结果; } /** *防止施工。 */ 专用缓冲区() { } }


    我还向Oracle提交了一个功能请求:

    +1。不错的良好实践问题。听起来收件人需要的是一个写时拷贝字节缓冲区。即使构造函数收到一个只读缓冲区,它也无法保证缓冲区不会被原始所有者(保留写访问权)修改。所以你还是被迫做一份防御性的拷贝。