将Long类型转换为byte[],然后在Java中再转回去应该如何操作?

212

如何在Java中将long转换为byte[],并再次转换回来?

我正在尝试将long转换为byte[],以便能够通过TCP连接发送byte[]。 在另一端,我想将该byte[]转换回long


1
另一个选择是Maps.transformValues,这是一种通用的工具,用于转换集合。http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html - Raul
1
如果您的目标是将长整型转换为最少数量的Base64字符,请参见https://dev59.com/3obca4cB1Zd3GeqPSzak。 - rogerdpack
18个回答

286
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();
}

或者将其包装在一个类中,以避免重复创建ByteBuffer:

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这样的库。如果您对库有某种奇怪的反感,那么您可能应该先考虑这个答案来寻找本地Java解决方案。我认为我回答中的主要优点是您不必自己担心系统的字节序问题。

3
聪明...但是每次转换都要创建和丢弃一个临时的ByteBuffer,如果您发送多个long值或大量消息,则不好。 - Stephen C
2
@Stephen - 我只是做了足够的工作来演示如何使用ByteBuffer,但我继续添加了一个在实用类中使用它的示例。 - Brad Mace
9
这里的bytesToLong()可能会失败,因为在put之后的位置是在缓冲区末尾而不是开头。我认为你将会得到一个缓冲区下溢异常。 - Alex Miller
12
在Java 8之前,您可以使用Long.SIZE/Byte.SIZE代替Long.BYTES,以避免魔数。 - jvdbogae
18
那个字节缓冲区的重复使用非常有问题,不仅是因为其他人评论的线程安全原因。不仅需要在中间调用'.clear()',而且不明显的是在 ByteBuffer 上调用.array() 返回支持数组而不是副本。因此,如果您重复调用并保留其他结果,则它们实际上都是相同的数组,反复覆盖先前的值。下面评论中的 hadoop 链接最高效,并避免了所有这些问题。 - Aaron Zinman
显示剩余4条评论

112

105

我测试了使用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;
}

1
我们可以使用result |= b[i];来代替result |= (b[i] & 0xFF);,因为对于位运算而言,与0xFF相与不会改变任何东西。 - Vipul
7
位运算符“&”很重要,因为仅使用“result |= b[i]”时,字节值将首先转换为long类型,这会导致符号扩展。 值为-128(十六进制为0x80)的字节将变成值为-128(十六进制为0xFFFF FFFF FFFF FF80)的长整型。 在转换后,值被合并。 使用按位与可以防止这种情况,因为它首先将字节转换为int类型并截断符号扩展:“(byte)0x80&0xFF ==>(int)0xFFFFFF80&0xFF ==>(int)0x80”。 为什么Java中的字节是带符号的我不太清楚,但我想这是为了适应其他类型。 - Brainstorm
@Brainstorm 我尝试了使用 |= b[i] 和 |= (b[i] & 0xFF) 处理 -128 的情况,结果是一样的!! - Mahmoud Hanafy
问题是字节在进行移位之前会被提升,这会导致符号位出现奇怪的问题。因此,我们首先通过按位与(&)0xFF来获取正确的值以进行移位。 - Wytze
1
左移 Long.BYTES 是错误的。它不能传达意图,只是巧合起作用:Long.BYTES 是8。如果用Integer替换,它也会出错。相反,应该左移 Byte.SIZE - Christian Hujer
显示剩余4条评论

48

如果您正在寻找快速展开版本,只需使用以下代码即可,假设有一个长度为8的byte数组名为“b”:

byte[] -> long

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)};

1
谢谢,最棒的! - Miha_x64
1
这假设使用小端字节顺序,并且由其他人在后续编辑中添加的 long -> byte[] 代码对于负数无效 :-/ - dnault

16
为什么需要byte[]?为什么不能直接将其写入套接字?
我假设您指的是long而不是Long,后者需要允许空值。
DataOutputStream dos = new DataOutputStream(
     new BufferedOutputStream(socket.getOutputStream()));
dos.writeLong(longValue);

DataInputStream dis = new DataInputStream(
     new BufferedInputStream(socket.getInputStream()));
long longValue = dis.readLong();

8
他问如何将数据转换为byte[]并再次转回。回答不错,但没有回答问题。你问为什么,因为你认为这是不必要的,但这是错误的假设。如果他正在进行跨语言或跨平台操作,DataOutputStream并不能帮助你。 - user1132959
6
如果他在进行跨语言或跨平台操作,则按已知顺序发送字节很重要。根据文档,该方法采用“高字节优先”(即先写高位字节),以确保顺序。而被接受的答案则没有这样做(它按照文档中的“当前顺序”写入字节)。问题说明了他想通过TCP连接发送数据。byte[] 只是实现这个目的的一种手段。 - Ian McLaird
3
接受的答案确实使用了一种已知的顺序。它创建了一个新的 ByteBuffer,根据文档的说法,“字节缓冲区的初始顺序始终为 BIG_ENDIAN(大端序)。” - David Phillips

14

我觉得这种方法最友好。

byte[] b = BigInteger.valueOf(x).toByteArray();

long l = new BigInteger(b).longValue();

11

目前所有的回答都比必要的复杂,我不希望任何人在找到这个线程后没有更简洁的选项就离开。

你可以在一行代码中完成这两个转换。

byte[] 转 long:

ByteBuffer.wrap(yourBytes).getLong();

将long转换为byte[]:

ByteBuffer.wrap(new byte[8]).putLong(yourLong).array();

4

只需将长整型写入具有底层ByteArrayOutputStreamDataOutputStream中。通过toByteArray()方法,您可以从ByteArrayOutputStream获取字节数组:

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 ..

代替之。

4

为什么这个实现使用异或(^=)而不是或(OR)? https://github.com/apache/hbase/blob/master/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Bytes.java - victtim

3
 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;
    }

2
公共静态字节数组 longToBytes(long l) { 长 num = l; 字节[] bytes = 新的字节[8]; 对于 (int i = bytes.length - 1; i >= 0; i--) { bytes[i] = (byte)(num & 0xff); num >>= 8; } 返回字节; } - eckes
原始方法无法处理负数,因为在 while (l != 0) 中会进入无限循环。@eckes的方法无法处理大于127的数字,因为他没有考虑到字节超过127会变成负数,因为它们是有符号的。 - Big_Bad_E

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接