6

也许我以错误的方式解决这个问题,但我错过了一些绝对的 put 方法ByteBuffer

如果您看一下,ByteBuffer您会发现大多数 put 方法都有绝对变体和相对变体。

除了:

  • byte数组的一部分写入ByteBuffer.
  • 将 aByteBuffer写入ByteBuffer.

..而我正是需要这些。

要清楚ByteBuffer有方法:

 put(byte[] src, int offset, int length)
 put(ByteBuffer src)

但缺乏:

 put(int index, byte[] src, int offset, int length)
 put(int index, ByteBuffer src)

我有理由不想移动缓冲区的位置指针,因此我只想使用绝对 put 方法。

知道为什么这些方法被遗漏了吗?

我当然可以在不移动缓冲区的位置指针的情况下模仿丢失的方法,但这将涉及循环源字节。Javadoc 明确指出,这些方法(可能)比移动比循环和逐个移动字节更有效。我相信 Javadoc,因为我的测试表明相同。我需要尽可能多地从我的实现中挤出速度,因此我当然倾向于利用我可以得到的任何批量方法......如果它们存在的话。

IncedentiallyByteBuffer也缺少get用于部分字节数组移动的绝对方法。但我目前实际上并不需要这种方法。但又奇怪它不存在。

4

4 回答 4

4

获得所需方法的一种方法是让第二个 ByteBuffer 共享相同的内存,这样您就可以在不更改原始位置的情况下更改其位置。

不幸的是,该slice方法也没有位置参数。相反,它使用原始缓冲区的当前位置。所以你不能这样做:

dstBuffer.slice(100).put(srcBuffer);

这里有一些想法,没有特别的顺序,除了我想到的顺序:

  • 如果它适合您使用缓冲区的方式,您可以准备一份缓冲区副本,slice()并在您需要将数据放置在与原始位置无关的位置时保留它。

  • 如果您要绝对放置的位置始终大于或等于原始缓冲区的位置指针,您可以执行以下操作:

    dstBuffer.slice().position(desiredPosition - dstBuffer.position()).put(srcBuffer);
    

不幸的是,这不能放在更早的位置,因为切片上的位置不允许为负数。 编辑:没关系,我忘记了重复方法。请参阅@BorisBrodski 的最佳答案。

  • 如果您不使用直接字节缓冲区,则 System.arraycopy 既简单又快速:

    System.arraycopy(
        srcBuffer.array(), srcBuffer.arrayOffset() + srcBuffer.position(),
        dstBuffer.array(), dstBuffer.arrayOffset() + desiredPosition,
        srcBuffer.remaining()
    );
    
  • 如果不需要并发访问,则可以在需要进行绝对放置时临时更改缓冲区的位置,然后再将其放回。如果您需要并发访问但线程争用较低,则可以同步对缓冲区的所有访问(可能很明显,但为了完整性而包含在内):

    synchronize (lock) {
        int originalPosition = dstBuffer.position();
        dstBuffer.position(desiredPosition);
        dstBuffer.put(srcBuffer);
        dstBuffer.position(originalPosition);
    }
    
  • 如果其他想法都不适合您,您可以破解缓冲区。这很混乱,但这里有一个例子:

    private static final sun.misc.Unsafe UNSAFE;
    static {
        Object result = null;
        try {
            Class<?> klass = Class.forName("sun.misc.Unsafe");
            for (Field field : klass.getDeclaredFields()) {
                if (field.getType() == klass &&
                    (field.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) ==
                        (Modifier.FINAL | Modifier.STATIC)) {
                    field.setAccessible(true);
                    result = field.get(null);
                    break;
                }
            }
        } catch (Throwable t) {}
        UNSAFE = result == null ? null : (sun.misc.Unsafe)result;
    }
    
    private static final Field ADDRESS_FIELD;
    static {
        Field f;
        try {
            f = Buffer.class.getDeclaredField("address");
            f.setAccessible(true);
        } catch (NoSuchFieldException | SecurityException e) {
            f = null;
        }
        ADDRESS_FIELD = f;
    }
    
    
    public static void absolutePut(ByteBuffer dstBuffer, int dstPosition, ByteBuffer srcBuffer) {
        if (!srcBuffer.isDirect()) {
            absolutePut(dstBuffer, dstPosition,
                srcBuffer.array(), srcBuffer.arrayOffset() + srcBuffer.position(),
                srcBuffer.remaining());
            return;
        }
    
        if (UNSAFE != null && ADDRESS_FIELD != null && dstBuffer.isDirect()) {
            try {
                long dstAddress = (long)ADDRESS_FIELD.get(dstBuffer) + dstPosition;
                long srcAddress = (long)ADDRESS_FIELD.get(srcBuffer) + srcBuffer.position();
                UNSAFE.copyMemory(srcAddress, dstAddress, srcBuffer.remaining());
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        } else {
            // fallback to basic loop
            for (int i = srcBuffer.position(); i < srcBuffer.limit(); i++) {
                dstBuffer.put(dstPosition + i, srcBuffer.get(i));
            }
        }
    }
    
    public static void absolutePut(ByteBuffer dstBuffer, int dstPosition, byte[] src, int srcOffset, int length) {
        if (UNSAFE != null && ADDRESS_FIELD != null && dstBuffer.isDirect()) {
            try {
                long dstAddress = (long)ADDRESS_FIELD.get(dstBuffer) + dstPosition;
                UNSAFE.copyMemory(
                    src, UNSAFE.arrayBaseOffset(byte[].class) + srcOffset,
                    null, dstAddress,
                    length);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        } else {
            // fallback to System.arraycopy
            System.arraycopy(
                src, srcOffset,
                dstBuffer.array(), dstBuffer.arrayOffset() + dstPosition,
                length);
        }
    }
    

我已经用直接和非直接缓冲区的混合对这段代码进行了一些最小的测试,看起来还可以。如果反射技术失败(例如,因为您在 applet 安全沙箱中或 Java 实现不兼容),它可以退回到更简单的方法。

于 2013-03-14T16:27:03.573 回答
3

在我看来,那些方法被遗漏了,因为不存在其他这样的方法。有一堆绝对方法采用包括“字节”在内的基元,但没有采用基元数组的绝对方法。

解决方案可能是:

((ByteBuffer)buffer.duplicate().position(index)).put(src, offset, length);
((ByteBuffer)buffer.duplicate().position(index)).put(otherByteBuffer);
于 2013-03-14T16:39:26.027 回答
2
static void put(ByteBuffer buffer, int index, ByteBuffer src)
{
    int p0 = buffer.position();
    buffer.position(index);
    buffer.put(src);
    buffer.position(p0);
}
于 2013-03-14T15:17:18.890 回答
0

这是我用于绝对批量的内容get()

// Perform a bulk get() that doesn't modify the buffer
public void get(ByteBuffer buf, int position, byte[] dest, int off, int len) {
    if (buf.hasArray())
        System.arraycopy(buf.array(), buf.arrayOffset() + position, dest, off, len);
    else if (len < 256) {        // 256 is a wild guess TODO: performance testing
        while (len-- > 0)
            dest[off++] = buf.get(position++);
    } else
        ((ByteBuffer)buf.duplicate().position(position)).get(dest, off, len);
}

类似的方法适用于put().

于 2017-02-13T21:18:52.893 回答