也许我正在以错误的方式解决这个问题,但我错过了一些绝对的 put方法ByteBuffer.
如果你看一下,ByteBuffer你会发现大多数put方法都有绝对变量和相对变量.
除了:
byte数组的部分写入ByteBuffer.ByteBuffer到ByteBuffer...而且我需要那些.
要明确ByteBuffer有方法:
put(byte[] src, int offset, int length)
put(ByteBuffer src)
Run Code Online (Sandbox Code Playgroud)
但缺乏:
put(int index, byte[] src, int offset, int length)
put(int index, ByteBuffer src)
Run Code Online (Sandbox Code Playgroud)
我有理由为什么我不想移动缓冲区的位置指针,因此我只想使用绝对put方法.
知道为什么这些方法被遗漏了吗?
我当然可以在不移动缓冲区位置指针的情况下模仿丢失的方法,但这将涉及在源字节上循环.Javadoc明确指出,这些方法(可能)比移动更有效,而不是循环并逐个移动字节.我相信Javadoc,因为我的测试表明相同.我需要从实施中尽可能多地挤出速度,因此我当然倾向于利用我可以获得的任何批量方法......如果它们存在的话.
Incedentially ByteBuffer也缺少get部分字节数组移动的绝对方法.但我目前实际上并不需要这样的方法.但又奇怪的是它不存在.
获取所需方法的一种方法是让第二个 ByteBuffer 共享相同的内存,这样您就可以更改其位置,而无需更改原始位置。
不幸的是,该slice方法也不采用位置参数;相反,它使用原始缓冲区的当前位置。所以你不能这样做:
dstBuffer.slice(100).put(srcBuffer);
Run Code Online (Sandbox Code Playgroud)
以下是一些想法,没有特定的顺序,只是我想到的顺序:
如果它适合您使用缓冲区的方式,您可以准备缓冲区的副本并将slice()其保留下来,以备需要将数据放置在独立于原始位置的位置时使用。
如果您想要绝对放置的位置始终大于或等于原始缓冲区的位置指针,您可以执行以下操作:
dstBuffer.slice().position(desiredPosition - dstBuffer.position()).put(srcBuffer);
Run Code Online (Sandbox Code Playgroud)不幸的是,这不能放在较早的位置,因为切片上的位置不允许为负数。 编辑:没关系,我忘记了重复方法。请参阅@BorisBrodski 的精彩答案。
如果您不使用直接字节缓冲区,则 System.arraycopy 既简单又快速:
System.arraycopy(
srcBuffer.array(), srcBuffer.arrayOffset() + srcBuffer.position(),
dstBuffer.array(), dstBuffer.arrayOffset() + desiredPosition,
srcBuffer.remaining()
);
Run Code Online (Sandbox Code Playgroud)如果不需要并发访问,那么您可以在需要进行绝对放置时临时更改缓冲区的位置,然后再将其放回去。如果您需要并发访问但线程争用较低,则可以同步对缓冲区的所有访问(可能很明显,但为了完整性而包括在内):
synchronize (lock) {
int originalPosition = dstBuffer.position();
dstBuffer.position(desiredPosition);
dstBuffer.put(srcBuffer);
dstBuffer.position(originalPosition);
}
Run Code Online (Sandbox Code Playgroud)如果其他想法都不适合您,您可以破解缓冲区。这很混乱,但这里有一个例子:
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);
}
}
Run Code Online (Sandbox Code Playgroud)我已经对这段代码进行了一些混合直接和非直接缓冲区的最小测试,看起来还不错。如果反射技术失败(例如,因为您位于小程序安全沙箱中或 Java 实现不兼容),它可以回退到更简单的方法。
| 归档时间: |
|
| 查看次数: |
1949 次 |
| 最近记录: |