我有一个长字节数组,代表一个大图片的abgr32
像素数据(0xAABBGGRR
)。有没有有效的方法来更改Java中此长字节数组的连续性?
例如:
源字节数组是[FF,11,22,33,FF,12,34,11,..........
]
是否可以将其转换为类似[33,22,11,FF,11,34,12,FF,....
]的内容?效率很重要。
我尝试使用ByteBuffer.order(ByteOrder.LITTLE_ENDIAN
)和ByteBuffer.order(ByteOrder.BIG_ENDIAN)
转换,但没有帮助。
谢谢您的帮助。
您可以尝试通过交换相邻4个字节的每组中的字节来手动进行操作。此代码假定数组的长度是4的倍数。
for (int i = 0; i < arr.length; i += 4) {
int tmp;
// swap 0 and 3
tmp = arr[i];
arr[i] = arr[i + 3];
arr[i + 3] = tmp;
// swap 1 and 2
tmp = arr[i + 1];
arr[i + 1] = arr[i + 2];
arr[i + 2] = tmp;
}
尽管从技术上讲这是解决问题的方法,但找到对字节序不敏感的解决方案(如上述某些评论)可能更明智。
首先:确定要更改它吗?原生Java BufferedImage.TYPE_4BYTE_ABGR
应该可以在这种像素布局下正常工作...
现在,如果您确实需要更改字节顺序,则使用ByteBuffer.order(ByteOrder.LITTLE_ENDIAN)
将数组包装在字节缓冲区中。只需记住字节缓冲区中的您必须读取/获取整数,在这种情况下字节顺序才有意义。更改字节顺序不会更改字节的内部顺序,而只是更改多字节类型(short, int, long
)的方式。这两个版本均不对数组进行重新排序,因此可能比进行任何重新排序的速度更快。如果您确实要重新排序,请在线程中查看其他答案。
public static void swapIntBytes(byte[] bytes) {
assert bytes.length % 4 == 0;
for (int i = 0; i < bytes.length; i += 4) {
// swap 0 and 3
byte tmp = bytes[i];
bytes[i] = bytes[i + 3];
bytes[i + 3] = tmp;
// swap 1 and 2
byte tmp2 = bytes[i + 1];
bytes[i + 1] = bytes[i + 2];
bytes[i + 2] = tmp2;
}
}
public static void swapIntBytes2(byte[] bytes) {
assert bytes.length % 4 == 0;
IntBuffer bb1 = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
IntBuffer bb2 = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
bb1.put(bb2);
}
public static void swapIntBytes3(byte[] bytes) {
assert bytes.length % 4 == 0;
for (int i = 0; i < bytes.length; i += 4)
UNSAFE.putInt(bytes, BYTES_OFFSET + i, Integer.reverseBytes(UNSAFE.getInt(bytes, BYTES_OFFSET + i)));
}
public static void main(String... ignored) {
byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8};
swapIntBytes3(bytes);
System.out.println(Arrays.toString(bytes));
byte[] bytes4k = new byte[16384];
for (int i = 0; i < 20; i++) {
long start = System.nanoTime();
swapIntBytes(bytes4k);
long mid1 = System.nanoTime();
swapIntBytes2(bytes4k);
long mid2 = System.nanoTime();
swapIntBytes3(bytes4k);
long end = System.nanoTime();
System.out.printf("Swap byte[] %.1f us, Swap IntBuffer %.1f us, Swap UNSAFE %.1f%n",
(mid1 - start) / 1e3, (mid2 - mid1) / 1e3, (end - mid2) / 1e3);
}
}
static final Unsafe UNSAFE;
static final int BYTES_OFFSET;
static {
try {
@SuppressWarnings("ALL")
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
BYTES_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
} catch (Exception e) {
throw new AssertionError(e);
}
}
打印
[4, 3, 2, 1, 8, 7, 6, 5] Swap byte[] 433.8 us, Swap IntBuffer 8577.8 us, Swap UNSAFE 1711.8 Swap byte[] 244.6 us, Swap IntBuffer 5773.1 us, Swap UNSAFE 1764.0 Swap byte[] 247.5 us, Swap IntBuffer 5059.7 us, Swap UNSAFE 1756.3 Swap byte[] 255.7 us, Swap IntBuffer 416.9 us, Swap UNSAFE 563.6 Swap byte[] 441.7 us, Swap IntBuffer 437.6 us, Swap UNSAFE 597.4 Swap byte[] 12.1 us, Swap IntBuffer 466.9 us, Swap UNSAFE 632.7 Swap byte[] 11.6 us, Swap IntBuffer 448.4 us, Swap UNSAFE 7.8 Swap byte[] 10.7 us, Swap IntBuffer 415.4 us, Swap UNSAFE 7.0 Swap byte[] 10.2 us, Swap IntBuffer 426.1 us, Swap UNSAFE 6.6 Swap byte[] 9.1 us, Swap IntBuffer 49.0 us, Swap UNSAFE 3.4 Swap byte[] 5.9 us, Swap IntBuffer 24.4 us, Swap UNSAFE 3.1 Swap byte[] 6.0 us, Swap IntBuffer 24.3 us, Swap UNSAFE 3.2 Swap byte[] 5.9 us, Swap IntBuffer 24.2 us, Swap UNSAFE 3.1 Swap byte[] 5.9 us, Swap IntBuffer 24.2 us, Swap UNSAFE 3.1 Swap byte[] 5.9 us, Swap IntBuffer 24.1 us, Swap UNSAFE 3.1 Swap byte[] 5.9 us, Swap IntBuffer 24.2 us, Swap UNSAFE 3.1 Swap byte[] 5.9 us, Swap IntBuffer 24.2 us, Swap UNSAFE 3.1 Swap byte[] 6.6 us, Swap IntBuffer 27.1 us, Swap UNSAFE 3.5 Swap byte[] 5.9 us, Swap IntBuffer 24.2 us, Swap UNSAFE 3.1 Swap byte[] 6.7 us, Swap IntBuffer 27.1 us, Swap UNSAFE 3.5
我只想进行一次简单的迭代:
for (int i=0; i<array.length; i+=4) { int sum03 = array[i+0]+array[i+3]; int sum12 = array[i+1]+array[i+2]; array[i+0] = sum03 - array[i+0]; array[i+1] = sum12 - array[i+1]; array[i+2] = sum12 - array[i+2]; array[i+3] = sum03 - array[i+3]; }
如果您知道如何告诉编译器进行某种循环展开优化,它可能比“常规交换”更快。本页上列出的方法似乎都不像BitcoinJ source code中的方法那样简单:
/** * Returns a copy of the given byte array in reverse order. */ public static byte[] reverseBytes(byte[] bytes) { // We could use the XOR trick here but it's easier to understand if we don't. If we find this is really a // performance issue the matter can be revisited. byte[] buf = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) buf[i] = bytes[bytes.length - 1 - i]; return buf; }