如何将 long
转换为 byte[]
并返回 Java?
我正在尝试将 long
转换为 byte[]
,以便能够通过 TCP 连接发送 byte[]
。另一方面,我想把那个 byte[]
转换回 double
。
public byte[] longToBytes(long x) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(x);
return buffer.array();
}
public long bytesToLong(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.put(bytes);
buffer.flip();//need flip
return buffer.getLong();
}
或者包装在一个类中以避免重复创建 ByteBuffers:
public class ByteUtils {
private static ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
public static byte[] longToBytes(long x) {
buffer.putLong(0, x);
return buffer.array();
}
public static long bytesToLong(byte[] bytes) {
buffer.put(bytes, 0, bytes.length);
buffer.flip();//need flip
return buffer.getLong();
}
}
由于这变得如此流行,我只想提一下,我认为在绝大多数情况下你最好使用像 Guava 这样的库。如果您对库有一些奇怪的反对意见,您可能应该首先考虑将 this answer 用于本机 Java 解决方案。我认为我的回答真正要解决的主要问题是您不必自己担心系统的字节序。
我针对普通的按位运算测试了 ByteBuffer 方法,但后者明显更快。
public static byte[] longToBytes(long l) {
byte[] result = new byte[8];
for (int i = 7; i >= 0; i--) {
result[i] = (byte)(l & 0xFF);
l >>= 8;
}
return result;
}
public static long bytesToLong(final byte[] b) {
long result = 0;
for (int i = 0; i < 8; i++) {
result <<= 8;
result |= (b[i] & 0xFF);
}
return result;
}
对于 Java 8+,我们可以使用添加的静态变量:
public static byte[] longToBytes(long l) {
byte[] result = new byte[Long.BYTES];
for (int i = Long.BYTES - 1; i >= 0; i--) {
result[i] = (byte)(l & 0xFF);
l >>= Byte.SIZE;
}
return result;
}
public static long bytesToLong(final byte[] b) {
long result = 0;
for (int i = 0; i < Long.BYTES; i++) {
result <<= Byte.SIZE;
result |= (b[i] & 0xFF);
}
return result;
}
result |= b[i]
时,字节值将首先转换为 long 进行符号扩展。值为 -128(十六进制 0x80
)的字节将变成值为 -128(十六进制 0xFFFF FFFF FFFF FF80
)的长整数。转换后首先是值 or:ed 在一起。使用 bitwise-and 通过首先将字节转换为 int 并切断符号扩展名来防止这种情况:(byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80
。为什么字节在 java 中被签名对我来说有点神秘,但我想它是为了适应其他类型。
如果您正在寻找一个快速展开的版本,这应该可以解决问题,假设一个名为“b”的字节数组长度为 8:
字节[] -> 长
long l = ((long) b[7] << 56)
| ((long) b[6] & 0xff) << 48
| ((long) b[5] & 0xff) << 40
| ((long) b[4] & 0xff) << 32
| ((long) b[3] & 0xff) << 24
| ((long) b[2] & 0xff) << 16
| ((long) b[1] & 0xff) << 8
| ((long) b[0] & 0xff);
long -> byte[] 与上述完全对应
byte[] b = new byte[] {
(byte) lng,
(byte) (lng >> 8),
(byte) (lng >> 16),
(byte) (lng >> 24),
(byte) (lng >> 32),
(byte) (lng >> 40),
(byte) (lng >> 48),
(byte) (lng >> 56)};
long -> byte[]
代码因负数而失败:-/
为什么需要字节[]?为什么不直接将其写入套接字?
我假设您的意思是长而不是长,后者需要允许空值。
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
dos.writeLong(longValue);
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
long longValue = dis.readLong();
byte[]
只是达到此目的的一种手段。
ByteBuffer
“字节缓冲区的初始顺序始终为 BIG_ENDIAN。
我觉得这种方法最友好。
var b = BigInteger.valueOf(x).toByteArray();
var l = new BigInteger(b);
只需将 long 写入具有基础 ByteArrayOutputStream 的 DataOutputStream。从 ByteArrayOutputStream 中,您可以通过 toByteArray() 获取字节数组:
class Main
{
public static byte[] long2byte(long l) throws IOException
{
ByteArrayOutputStream baos=new ByteArrayOutputStream(Long.SIZE/8);
DataOutputStream dos=new DataOutputStream(baos);
dos.writeLong(l);
byte[] result=baos.toByteArray();
dos.close();
return result;
}
public static long byte2long(byte[] b) throws IOException
{
ByteArrayInputStream baos=new ByteArrayInputStream(b);
DataInputStream dos=new DataInputStream(baos);
long result=dos.readLong();
dos.close();
return result;
}
public static void main (String[] args) throws java.lang.Exception
{
long l=123456L;
byte[] b=long2byte(l);
System.out.println(l+": "+byte2long(b));
}
}
相应地适用于其他原语。
提示:对于 TCP,您不需要手动使用 byte[]。您将使用 Socket socket
及其流
OutputStream os=socket.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
dos.writeLong(l);
//etc ..
反而。
您可以使用 org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html 中的实现
源代码在这里:
寻找 toLong 和 toBytes 方法。
我相信软件许可证允许您获取部分代码并使用它,但请验证。
public static long bytesToLong(byte[] bytes) {
if (bytes.length > 8) {
throw new IllegalMethodParameterException("byte should not be more than 8 bytes");
}
long r = 0;
for (int i = 0; i < bytes.length; i++) {
r = r << 8;
r += bytes[i];
}
return r;
}
public static byte[] longToBytes(long l) {
ArrayList<Byte> bytes = new ArrayList<Byte>();
while (l != 0) {
bytes.add((byte) (l % (0xff + 1)));
l = l >> 8;
}
byte[] bytesp = new byte[bytes.size()];
for (int i = bytes.size() - 1, j = 0; i >= 0; i--, j++) {
bytesp[j] = bytes.get(i);
}
return bytesp;
}
我将添加另一个可能最快的答案 ׂ(是的,甚至比公认的答案还要多),但它不适用于每种情况。但是,它适用于所有可能的情况:
您可以简单地使用 String 作为中间体。请注意,这将为您提供正确的结果,即使看起来使用 String 可能会产生错误的结果,只要您知道您正在使用“正常”字符串。这是一种提高效率并使代码更简单的方法,作为回报,它必须对其操作的数据字符串使用一些假设。
使用此方法的缺点:如果您在 ASCII 表的开头使用一些 ASCII 字符,例如这些符号,以下行可能会失败,但让我们面对现实吧 - 您可能永远不会使用它们。
使用此方法的优点:请记住,大多数人通常使用一些没有任何异常字符的正常字符串,然后该方法是最简单和最快的方法。
从 Long 到 byte[]:
byte[] arr = String.valueOf(longVar).getBytes();
从 byte[] 到 Long:
long longVar = Long.valueOf(new String(byteArr)).longValue();
对于 Long 和 ByteArray 类型的 Kotlin 扩展:
fun Long.toByteArray() = numberToByteArray(Long.SIZE_BYTES) { putLong(this@toByteArray) }
private inline fun numberToByteArray(size: Int, bufferFun: ByteBuffer.() -> ByteBuffer): ByteArray =
ByteBuffer.allocate(size).bufferFun().array()
@Throws(NumberFormatException::class)
fun ByteArray.toLong(): Long = toNumeric(Long.SIZE_BYTES) { long }
@Throws(NumberFormatException::class)
private inline fun <reified T: Number> ByteArray.toNumeric(size: Int, bufferFun: ByteBuffer.() -> T): T {
if (this.size != size) throw NumberFormatException("${T::class.java.simpleName} value must contains $size bytes")
return ByteBuffer.wrap(this).bufferFun()
}
您可以在我的库中查看完整代码 https://github.com/ArtemBotnev/low-level-extensions
如果您已经在使用 OutputStream
写入套接字,那么 DataOutputStream 可能是一个不错的选择。这是一个例子:
// Assumes you are currently working with a SocketOutputStream.
SocketOutputStream outputStream = ...
long longValue = ...
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeLong(longValue);
dataOutputStream.flush();
short
、int
、float
等也有类似的方法。然后您可以在接收端使用 DataInputStream。
这是使用 Java 8 或更高版本将 byte[]
转换为 long
的另一种方法:
private static int bytesToInt(final byte[] bytes, final int offset) {
assert offset + Integer.BYTES <= bytes.length;
return (bytes[offset + Integer.BYTES - 1] & 0xFF) |
(bytes[offset + Integer.BYTES - 2] & 0xFF) << Byte.SIZE |
(bytes[offset + Integer.BYTES - 3] & 0xFF) << Byte.SIZE * 2 |
(bytes[offset + Integer.BYTES - 4] & 0xFF) << Byte.SIZE * 3;
}
private static long bytesToLong(final byte[] bytes, final int offset) {
return toUnsignedLong(bytesToInt(bytes, offset)) << Integer.SIZE |
toUnsignedLong(bytesToInt(bytes, offset + Integer.BYTES));
}
转换 long
可以表示为两个整数值的高位和低位进行按位或。请注意,toUnsignedLong
来自 Integer
类,第一次调用 toUnsignedLong
可能是多余的。
正如其他人所提到的,相反的转换也可以展开。
从 Java 9 开始,最好的方法是使用 VarHandle
,它将从字节数组中读取,就好像它是一个长数组一样,为了提高性能,使 VarHandle
实例成为静态最终字段。
static final VarHandle HANDLE = MethodHandles.byteArrayViewVarHandle(Long.TYPE.arrayType(), ByteOrder.nativeOrder());
static long bytesToLong(byte[] bytes, int offset) {
return (long)HANDLE.get(bytes, offset);
}
static void longToBytes(byte[] bytes, int offset, long value) {
HANDLE.set(bytes, offset, value);
}
当前的所有答案都比它们需要的复杂得多,我讨厌任何找到此线程的人在没有更简洁的选择的情况下走开。
您可以在一行中完成这两种转换。
字节 [] 长:
ByteBuffer.wrap(yourBytes).getLong();
长到字节[]:
ByteBuffer.wrap(new byte[8]).putLong(yourLong).array();
static byte[] longToBytes(Long l) {
return (l + "").getBytes(StandardCharsets.UTF_8);
}