【问题标题】:How do I convert Long to byte[] and back in java如何将 Long 转换为 byte[] 并返回到 java
【发布时间】:2011-05-27 23:22:41
【问题描述】:

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

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

【问题讨论】:

标签: java type-conversion byte long-integer


【解决方案1】:
static byte[] longToBytes(Long l) {
    return (l + "").getBytes(StandardCharsets.UTF_8);
}

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我针对普通的按位运算测试了 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;
    }
    

    【讨论】:

    • 而不是结果 |= (b[i] & 0xFF);我们可以简单地使用 result |= b[i];和 0xFF 一样并不会修改任何东西。
    • @Vipul 按位-and 确实很重要,因为当只执行result |= b[i] 时,字节值将首先转换为 long 进行符号扩展。值为 -128 的字节(十六进制 0x80)将变成值为 -128 的长字节(十六进制 0xFFFF FFFF FFFF FF80)。转换后首先是值 or:ed 在一起。使用 bitwise-and 通过首先将字节转换为 int 并切断符号扩展名来防止这种情况:(byte)0x80 &amp; 0xFF ==&gt; (int)0xFFFF FF80 &amp; 0xFF ==&gt; (int) 0x80。为什么字节在java中被签名对我来说有点神秘,但我想它是为了适应其他类型。
    • @Brainstorm 我用 |= b[i] 和 |= (b[i] & 0xFF) 尝试了 case -128,结果是一样的!!
    • 问题是在应用移位之前字节被提升,这会导致符号位出现奇怪的问题。因此我们首先用 0xFF (&) 来获得正确的移位值。
    • 我尝试转换这个数据 (data = new byte[]{(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8} ;) 长,但它返回 false 值 -2619032330856274424,预期值为 989231983928329832
    【解决方案4】:

    我觉得这个方法最友好。

    var b = BigInteger.valueOf(x).toByteArray();
    
    var l = new BigInteger(b);
    

    【讨论】:

      【解决方案5】:

      用于 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

      【讨论】:

        【解决方案6】:

        这是使用 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 可能是多余的。

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

          • 谢谢你,最好的!
          • 这假定了 little-endian 字节顺序,并且其他人在后续编辑中添加的 long -&gt; byte[] 代码因负数而失败:-/
          【解决方案8】:
          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。如果您在每条消息和/或大量消息中发送多个长消息,那就不好了。
          • @Stephen - 我只是做了足够多的工作来演示如何使用 ByteBuffer,但我继续添加了一个在实用程序类中使用它的示例。
          • 我认为这里的 bytesToLong() 会失败,因为 put 之后的位置位于缓冲区的末尾,而不是开头。我想你会得到一个缓冲区下溢异常。
          • Pre-Java 8,您可以使用 Long.SIZE/Byte.SIZE 而不是 Long.BYTES 来避免幻数。
          • 重用该字节缓冲区存在很大问题,而不仅仅是因为其他人评论的线程安全原因。不仅在两者之间需要“.clear()”,而且不明显的是,在 ByteBuffer 上调用 .array() 会返回支持数组而不是副本。因此,如果您重复调用并保留其他结果,它们实际上都是重复覆盖先前值的相同数组。下面评论中的 hadoop 链接是性能最高的,可以避免任何这些问题。
          【解决方案9】:

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

          我假设您的意思是 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();
          

          【讨论】:

          • 他问你如何转换为 byte[] 并返回。很好的答案,但没有回答问题。你问为什么,因为你认为这是不必要的,但这是一个错误的假设。如果他在做跨语言或跨平台呢? DataOutputStream 帮不了你。
          • 如果他在做跨语言或跨平台,那么以已知顺序发送字节很重要。根据文档,这种方法可以做到这一点(它“先写高字节”)。接受的答案没有(它根据文档以“当前顺序”编写它们)。问题表明他想通过 TCP 连接发送它们。 byte[] 只是实现这一目标的一种手段。
          • @IanMcLaird 接受的答案确实使用已知顺序。它根据文档创建一个新的ByteBuffer “字节缓冲区的初始顺序始终为 BIG_ENDIAN。
          【解决方案10】:

          您可以使用Google Guava 中的Byte conversion methods

          例子:

          byte[] bytes = Longs.toByteArray(12345L);
          

          【讨论】:

            【解决方案11】:

            我将添加另一个可能最快的答案 ׂ(是的,甚至比公认的答案还要多),但它不适用于每种情况。但是,它适用于所有可能的场景:

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

            使用此方法的缺点:如果您在 ASCII 表的开头使用一些 ASCII 字符,例如这些符号,以下行可能会失败,但让我们面对现实吧 - 您可能会无论如何都不要使用它们。

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

            从 Long 到 byte[]:

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

            从字节[]到长:

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

            【讨论】:

            • 抱歉发布了 necroposting,但这是错误的。例如。 String.valueOf(0).getBytes()[0] == 0x30。惊喜! String#getBytes 将返回 ASCII 编码的数字符号,而不是数字:'0' != 0,但 '0' == 0x30
            • 好吧,也许如果您阅读了我的全部答案,那么您会看到我已经在答案本身中警告过它..
            • 啊,我错过了中间字节 [] 数据被视为(几乎)不透明的点。您的技巧将适用于这种情况。
            【解决方案12】:

            您可以使用 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 方法。

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

            【讨论】:

            【解决方案13】:
             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;
                }
            

            【讨论】:

            • 你可以跳过 ArrayList: public static byte[] longToBytes(long l) { long num = l;字节[]字节=新字节[8]; for (int i = bytes.length - 1, i >= 0; i--) { bytes[i] = (byte)(num & 0xff);数 >>= 8; } 返回字节; }
            • 原始方法不适用于负数,因为它在 while (l != 0) 中进入无限循环,@eckes 的方法不适用于超过 127 的数字,因为他没有考虑到超过 127 的字节数为负数,因为它们已签名。
            【解决方案14】:

            只需将 long 写入具有底层 ByteArrayOutputStreamDataOutputStream。从 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 ..
            

            改为。

            【讨论】:

              【解决方案15】:

              如果您已经在使用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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-06-16
                • 1970-01-01
                • 2018-05-02
                • 2016-02-28
                • 1970-01-01
                • 2011-01-13
                • 1970-01-01
                相关资源
                最近更新 更多