如何避免创建ByteBuffer的防御性副本?

19

我有一个类,它以ByteBuffer作为构造函数参数。 是否有一种方法可以避免进行防御性复制,以确保在此过程之后不修改缓冲区?

ByteBuffer.isReadOnly()不能保证原始所有者不修改缓冲区。 更糟糕的是,似乎没有一种方法可以对ByteBuffer进行子类化。 有什么想法吗?


1
+1. 很好的最佳实践问题。 - helios
听起来接收者需要的是一个写时复制的字节缓冲区。 - Andy Thomas
4个回答

4
唯一可行的方法就是像你说的那样,使用buf.asReadOnlyBuffer(),然后将其传递给构造函数。除此之外别无选择,虽然你也可以将内容复制到一个新的ByteBuffer中,然后再传递给构造函数。

1
即使构造函数接收只读缓冲区,它也无法保证缓冲区不会被原始所有者(保留写访问权限)修改。因此,您仍然需要进行防御性复制。 - Gili

2

目前我所能做到的最好程度如下:

/**
 * 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()
    {
    }
}

我也向 Oracle 提交了一个功能请求: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7130631


1

不避免复制,但也许可以:

  1. 使用预填充的预分配ByteBuffer池
  2. 允许作者类的构造函数允许传入ByteBuffer的“副本”,但是让类使用来自池中的ByteBuffer来移动Alloc/Dealloc成本到应用程序启动/关闭。只需以这种方式支付memcopy成本。

0

这并不能完全回答问题,但是对于某些用例(例如,如果您主要想强制实施“契约设计”),它可能足够好并且更有效率。对于其他用例,它将不起作用并且可能效率远低于预期。

在构造函数中,保存 ByteBuffer 的 hashCode:

final int originalBBHashCode = byteBuffer.hashCode();

然后,在代码的几个关键位置,您想要验证 ByteBuffer 是否已更改,请验证 byteBuffer.hashCode() == originalBBHashCode。如果不是,则抛出异常。坦白地说,我会倾向于抛出 ConcurrentModificationException,因为这是您所模拟的行为,但请自行决定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接