如何将Long转换为byte []并返回Java


159

如何在Java中将longa 转换为a byte[]并返回?

我正在尝试将a转换long为a,byte[]以便能够byte[]通过TCP连接发送。另一方面,我想byte[]将其转换为double


另一个选择是Maps.transformValues,这是用于转换集合的通用工具。docs.guava-libraries.googlecode.com/git-history/release/javadoc/…–
Raul

1
如果您的目标是将long转换为最少数量的Base64字符,请参见stackoverflow.com/q/27559449/32453
rogerdpack

也许应该强调的是,转换管道是'long-> byte []-> double',而不是'long-> byte []-> long-> double'。
Kirill Gamazkov

Answers:


230
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这样的库。而且,如果您对库有一些奇怪的反对意见,则可能应该首先针对本机Java解决方案考虑此答案。我认为我的答案真正要解决的主要问题是,您不必自己担心系统的字节序。


3
聪明...但是您每次创建并丢弃一个临时ByteBuffer。如果您每条消息和/或大量消息发送多个long,则不好。
斯蒂芬·C

1
@Stephen-我只是做了足够的示范来演示如何使用ByteBuffer,但是我继续在实用程序类中添加了使用它的示例。
布拉德·梅斯

8
我认为这里的bytesToLong()会失败,因为放置后的位置在缓冲区的末尾,而不是开头。我认为您会遇到缓冲区下溢异常。
亚历克斯·米勒

11
在Java 8之前的版本中,可以使用Long.SIZE / Byte.SIZE代替Long.BYTES来避免出现幻数。
jvdbogae 2014年

8
该字节缓冲区的重用是一个很大的问题,而不仅仅是其他人提到的线程安全性原因。两者之间不仅需要'.clear()',而且不明显的是在ByteBuffer上调用.array()返回的是后备数组而不是副本。因此,如果您反复调用并保留其他结果,则实际上它们都是重复覆盖先前值的同一数组。下面评论中的hadoop链接是性能最高的链接,可避免任何这些问题。
亚伦·辛曼


84

我针对普通的按位操作测试了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[] bytes, final int offset) {
    long result = 0;
    for (int i = offset; i < Long.BYTES + offset; i++) {
        result <<= Long.BYTES;
        result |= (bytes[i] & 0xFF);
    }
    return result;
}

1
而不是结果| =(b [i]&0xFF); 我们可以简单地使用结果| = b [i]; 与0xFF一样,不会修改任何内容。
Vipul

3
@Vipul按位与,这很重要,因为在执行result |= b[i]此操作时,仅将字节值首先转换为对符号进行扩展的long。值为-128(hex 0x80)的字节将变成值为-128(hex )的long 0xFFFF FFFF FFFF FF80。转换后的第一个是将值或在一起。使用按位并防止这通过首先将字节转换为int和切断符号扩展:(byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80。为什么在Java中对字节进行签名对我来说还是个谜,但是我想它可以与其他类型配合使用。
头脑风暴

@Brainstorm我用| = b [i]和| =(b [i]&0xFF)尝试了案例-128,结果是一样的!
Mahmoud Hanafy '16

问题是在应用移位之前字节被提升,这导致符号位出现奇怪的问题。因此,我们首先用0xFF对其进行(&)处理,以获取正确的移位值。
Wytze '16

我尝试将这些数据转换为long,但是返回假值-2619032330856274424,预期值为989231983928329832
jefry jacky

29

如果您正在寻找一个快速展开的版本,这应该可以解决问题,假设一个名为“ 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)};

1
谢谢你,最好的!
Miha_x64

15

为什么需要字节[]?为什么不只是将其写入套接字?

我假设您的意思是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 2013年

4
如果他正在跨语言或跨平台工作,则以已知顺序发送字节非常重要。此方法根据文档执行此操作(将它们“先写高字节”)。接受的答案不是(根据文档,它们以“当前顺序”写入它们)。问题表明他想通过TCP连接发送它们。这byte[]只是达到此目的的一种手段。
伊恩·麦克劳德

3
@IanMcLaird接受的答案确实使用已知顺序。它创建了一个新的ByteBuffer,其根据文档“字节缓冲区的初始顺序始终是BIG_ENDIAN。
大卫·菲利普斯

4

只需使用基础ByteArrayOutputStream将long写入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 ..

代替。


4

您可以在org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html中使用该实现

源代码在这里:

http://grepcode.com/file/repository.cloudera.com/content/repositories/releases/com.cloudera.hbase/hbase/0.89.20100924-28/org/apache/hadoop/hbase/util/Bytes.java# Bytes.toBytes%28long%29

寻找toLong和toBytes方法。

我相信该软件许可证允许您使用部分代码并使用它,但请进行验证。



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
您可以跳过ArrayList:public static byte [] longToBytes(long l){long num = l; byte []个字节=新的byte [8];for(int i = bytes.length-1,i> = 0; i--){bytes [i] =(byte)(num&0xff); num >> = 8; } return bytesp; }
eckes

原始方法不适用于负数,因为它在while(l!= 0)中进入无限循环,@ eckes的方法不适用于127以上的数字,因为他不考虑超过127的字节为负他们已经签名。
Big_Bad_E

2

我将添加另一个可能最快的答案(是,甚至比公认的答案还多),但是它不能在每种情况下都起作用。但是,它将适用于所有可能的方案:

您可以简单地使用String作为中间语言。请注意,即使您知道要使用“ NORMAL”字符串,只要使用String可能会产生错误的结果,这都会为您提供正确的结果。这是一种提高效率并使代码更简单的方法,作为回报,该方法必须在其操作的数据字符串上使用一些假设。

使用此方法的缺点:如果要在ASCII表的开头使用某些ASCII字符(如这些符号),则以下几行可能会失败,但让我们面对现实-您可能永远也不会使用它们。

使用此方法的优点请记住,大多数人通常使用一些没有任何异常字符的普通字符串,因此该方法是最简单,最快的方法。

从长到字节[]:

byte[] arr = String.valueOf(longVar).getBytes();

从byte []到Long:

long longVar = Long.valueOf(new String(byteArr)).longValue();

2
很抱歉,您必须进行尸检,但这是错误的。例如 String.valueOf(0).getBytes()[0] == 0x30。惊喜!String#getBytes将返回ASCII编码的数字符号,而不是数字:'0'!= 0,但'0'== 0x30
Kirill Gamazkov

嗯,也许如果你读了我的整个的答案,那么你会看到我在答案本身..警告它
Yonatan尼尔

1
啊,我错过了中间byte []数据被视为(几乎)不透明的观点。您的技巧将在这种情况下起作用。
Kirill Gamazkov

1

如果您已经在使用OutputStream写入套接字,则DataOutputStream可能是一个合适的选择。这是一个例子:

// Assumes you are currently working with a SocketOutputStream.

SocketOutputStream outputStream = ...
long longValue = ...

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

dataOutputStream.writeLong(longValue);
dataOutputStream.flush();

还有类似的方法shortintfloat,等你可以再使用DataInputStream所接收方。



0

这是转换byte[]long使用Java 8或更高版本的另一种方法:

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

转换a long可以表示为两个按位或的整数值的高位和低位。请注意,toUnsignedLong来自Integer类的is 和第一次调用to toUnsignedLong可能是多余的。

正如其他人所提到的,相反的转换也可以展开。


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.