6

おそらく私はこれを間違った方法で行っていますが、いくつかの絶対配置メソッドがありませんByteBuffer

見てみるとByteBuffer、ほとんどの put メソッドに絶対バリアントと相対バリアントの両方があることがわかります。

を除いて:

  • byte配列の一部を に書き込みますByteBuffer
  • ByteBufferに aを書き込み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)

バッファーの位置ポインターを移動したくない理由があるため、絶対配置メソッドのみを使用したいのです。

これらのメソッドが除外された理由は何ですか?

もちろん、バッファの位置ポインタを移動せずに不足しているメソッドを模倣することはできますが、それにはソース バイトのループが必要です。Javadoc は、これらのメソッドが (潜在的に) ループしてバイトを 1 つずつ移動するよりもはるかに効率的であると明確に述べています。私のテストが同じことを示しているので、私は Javadoc を信じています。実装からできるだけ多くの速度を絞り出す必要があるため、もちろん、手に入れることができるバルクメソッドを利用する傾向があります....それらが存在する場合.

ちなみに、部分的なバイト配列を移動するためByteBufferの絶対メソッドもありません。getしかし、私は現在、そのような方法を実際には必要としません。しかし、やはり奇妙なことに、それは存在しません。

4

4 に答える 4

4

必要なメソッドを取得する 1 つの方法は、同じメモリを共有する 2 つ目の 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);
        }
    }
    

このコードに、直接バッファと非直接バッファを組み合わせて最小限のテストを行いましたが、問題ないようです。リフレクション手法が失敗した場合 (たとえば、アプレット セキュリティ サンドボックス内にいる、または 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 に答える