Java 在ByteBuffer上缺少一些绝对方法

Java 在ByteBuffer上缺少一些绝对方法,java,Java,也许我走错了方向,但我缺少一些绝对的put方法 如果您查看ByteBuffer,您会发现大多数put方法都有绝对变量和相对变量 除了: 将字节数组的一部分写入字节缓冲区 将ByteBuffer写入ByteBuffer …我需要的正是这些 要清楚,ByteBuffer有以下方法: put(byte[] src, int offset, int length) put(ByteBuffer src) 但缺乏: put(int index, byte[] src, int offset,

也许我走错了方向,但我缺少一些绝对的put方法

如果您查看
ByteBuffer
,您会发现大多数put方法都有绝对变量和相对变量

除了:

  • 字节
    数组的一部分写入
    字节缓冲区
  • ByteBuffer
    写入
    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,因为我的测试也表明了这一点。我需要从我的实现中挤出尽可能多的速度,所以我当然倾向于利用我能得到的任何批量方法。。。。要是它们存在就好了


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

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

static void put(ByteBuffer buffer, int index, ByteBuffer src)
{
    int p0 = buffer.position();
    buffer.position(index);
    buffer.put(src);
    buffer.position(p0);
}
不幸的是,该方法也没有采用位置参数;相反,它使用原始缓冲区的当前位置。所以你不能这样做:

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()
    );
    
  • 如果不需要并发访问,则可以在需要执行绝对put时临时更改缓冲区的位置,然后再将其放回。如果您需要并发访问,但线程争用率较低,则可以同步对缓冲区的所有访问(可能很明显,但为了完整性而包括在内):

  • 如果其他想法都不适合你,你可以破解缓冲区。这很混乱,但这里有一个例子:

    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);
        }
    }
    
    private static final sun.misc.Unsafe;
    静止的{
    对象结果=空;
    试一试{
    Class klass=Class.forName(“sun.misc.Unsafe”);
    for(字段:klass.getDeclaredFields()){
    if(field.getType()==klass&&
    (field.getModifiers()&(Modifier.FINAL | Modifier.STATIC))==
    (修饰符.最终|修饰符.静态)){
    字段。setAccessible(true);
    结果=field.get(null);
    打破
    }
    }
    }捕获(可丢弃的t){}
    不安全=结果==空?空:(sun.misc.UNSAFE)结果;
    }
    专用静态最终字段地址\u字段;
    静止的{
    字段f;
    试一试{
    f=Buffer.class.getDeclaredField(“地址”);
    f、 setAccessible(true);
    }catch(NoSuchFieldException | SecurityException e){
    f=零;
    }
    地址_字段=f;
    }
    公共静态无效绝对输出(ByteBuffer dstBuffer、int dstPosition、ByteBuffer srcBuffer){
    如果(!srcBuffer.isDirect()){
    绝对输出(dstBuffer、dstPosition、,
    srcBuffer.array(),srcBuffer.arrayOffset()+srcBuffer.position(),
    srcBuffer.remaining());
    返回;
    }
    if(不安全!=null&&ADDRESS_字段!=null&&dstBuffer.isDirect()){
    试一试{
    long dstAddress=(long)ADDRESS_FIELD.get(dstBuffer)+dstPosition;
    long srcdaddress=(long)ADDRESS_FIELD.get(srcBuffer)+srcBuffer.position();
    copyMemory(srcAddress、dstAddress、srcBuffer.remaining());
    }捕获(非法访问例外e){
    抛出新的运行时异常(e);
    }
    }否则{
    //回退到基本循环
    对于(int i=srcBuffer.position();i

我用直接缓冲区和非直接缓冲区的混合对这段代码进行了一些最低限度的测试,它看起来很好。如果反射技术失败(例如,因为您在applet安全沙箱中,或者Java实现不兼容),它可以退回到更简单的方法
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);
    }
}
((ByteBuffer)buffer.duplicate().position(index)).put(src, offset, length);
((ByteBuffer)buffer.duplicate().position(index)).put(otherByteBuffer);
// 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);
}