Làm cách nào để chuyển đổi Long thành byte [] và quay lại trong java


159

Làm cách nào để chuyển đổi a longthành a byte[]và trở lại trong Java?

Tôi đang thử chuyển đổi longthành một byte[]để tôi có thể gửi byte[]qua kết nối TCP. Mặt khác tôi muốn lấy nó byte[]và chuyển đổi lại thành a double.


Một lựa chọn khác sẽ là Maps.transformValues, một công cụ chung để chuyển đổi các bộ sưu tập. docs.guava-lologists.googlecode.com/git-history/release/javadoc/ triệt
Raul

1
Xem thêm stackoverflow.com/q/27559449/32453 nếu mục tiêu của bạn là chuyển đổi dài thành số lượng ký tự Base64 ít nhất.
rogerdpack

Có lẽ cần nhấn mạnh rằng đường ống chuyển đổi là 'dài -> byte [] -> gấp đôi', không phải 'dài -> byte [] -> dài -> gấp đôi'.
Kirill Gamazkov 17/03/2017

Câu trả lời:


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

Hoặc được bao bọc trong một lớp để tránh tạo ByteBuffers nhiều lần:

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

Vì điều này đang trở nên rất phổ biến, tôi chỉ muốn đề cập rằng tôi nghĩ rằng bạn nên sử dụng một thư viện như Guava trong phần lớn các trường hợp. Và nếu bạn có một số sự phản đối kỳ lạ đối với các thư viện, có lẽ bạn nên xem xét câu trả lời này trước tiên cho các giải pháp java gốc. Tôi nghĩ rằng điều chính mà câu trả lời của tôi thực sự có được là bạn không phải lo lắng về tính chính xác của hệ thống.


3
Thông minh ... nhưng bạn tạo và loại bỏ ByteBuffer tạm thời cho mỗi chuyển đổi. Không tốt nếu bạn đang gửi nhiều tin nhắn dài cho mỗi tin nhắn và / hoặc nhiều tin nhắn.
Stephen C

1
@Stephen - Tôi vừa mới làm đủ để trình bày cách sử dụng ByteBuffer, nhưng tôi đã tiếp tục và thêm một ví dụ về việc sử dụng nó trong một lớp tiện ích.
Brad Mace

8
Tôi nghĩ rằng byteToLong () ở đây sẽ thất bại vì vị trí sau khi đặt ở cuối bộ đệm, không phải là bắt đầu. Tôi nghĩ rằng bạn sẽ nhận được một ngoại lệ bộ đệm.
Alex Miller

11
Pre-Java 8, bạn có thể sử dụng Long.SIZE / Byte.SIZE thay vì Long.BYTES để tránh số ma thuật.
jvdbogae

8
Việc sử dụng lại bytebuffer đó rất có vấn đề và không chỉ vì lý do an toàn luồng như những người khác nhận xét. Không chỉ cần một '.clear ()' ở giữa, mà điều không rõ ràng là việc gọi .array () trên ByteBuffer sẽ trả về mảng sao lưu so với bản sao. Do đó, nếu bạn gọi liên tục và giữ các kết quả khác, thực tế tất cả chúng đều là cùng một mảng được ghi đè lên các giá trị trước đó. Liên kết hadoop trong một bình luận dưới đây là hiệu quả nhất và tránh bất kỳ vấn đề nào trong số này.
Aaron Zinman


84

Tôi đã thử nghiệm phương pháp ByteBuffer chống lại các hoạt động bitwise đơn giản nhưng sau này nhanh hơn đáng kể.

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
Thay vì kết quả | = (b [i] & 0xFF); Chúng ta chỉ cần sử dụng kết quả | = b [i]; như và với 0xFF cho một chút không sửa đổi bất cứ điều gì.
Vipul

3
@Vipul Bitwise-và không thành vấn đề bởi vì khi thực hiện chỉ result |= b[i]giá trị byte trước tiên sẽ được chuyển đổi thành dài mà ký hiệu mở rộng. Một byte có giá trị -128 (hex 0x80) sẽ biến thành dài với giá trị -128 (hex 0xFFFF FFFF FFFF FF80). Đầu tiên sau khi chuyển đổi là các giá trị hoặc: ed với nhau. Sử dụng bitwise - và bảo vệ chống lại điều này bằng cách trước tiên chuyển đổi byte thành int và cắt phần mở rộng dấu : (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Tại sao byte được đăng nhập trong java là một bí ẩn đối với tôi, nhưng tôi đoán rằng nó phù hợp với các loại khác.
Động não

@Brainstorm Tôi đã thử trường hợp -128 với | = b [i] và với | = (b [i] & 0xFF) và kết quả là như nhau !!
Mahmoud Hanafy

Vấn đề là byte được thăng cấp trước khi dịch chuyển được áp dụng gây ra các vấn đề lạ với bit dấu. Do đó, chúng tôi trước tiên và (&) với 0xFF để có được giá trị chính xác để thay đổi.
Wytze

Tôi cố gắng chuyển đổi dữ liệu này (data = new byte [] {(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8};) giá trị sai -2619032330856274424, giá trị dự kiến ​​là 989231983928329832
jefry jacky

29

Nếu bạn đang tìm kiếm một phiên bản không được kiểm soát nhanh, điều này sẽ thực hiện thủ thuật, giả sử một mảng byte có tên là "b" với độ dài 8:

byte [] -> dài

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

dài -> byte [] làm đối tác chính xác ở trên

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
Cảm ơn bạn, tốt nhất!
Miha_x64

15

Tại sao bạn cần byte []? Tại sao không chỉ viết nó vào ổ cắm?

Tôi giả sử bạn có nghĩa là dài hơn là dài , cái sau cần cho phép giá trị null.

DataOutputStream dos = new DataOutputStream(
     new BufferedOutputStream(socket.getOutputStream()));
dos.writeLong(longValue);

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

8
Anh ấy hỏi làm thế nào bạn chuyển đổi sang byte [] và trở lại. Câu trả lời tốt nhưng không trả lời câu hỏi. Bạn hỏi tại sao bởi vì bạn cho rằng nó không cần thiết nhưng đó là một giả định sai. Nếu anh ta đang làm ngôn ngữ chéo hoặc đa nền tảng thì sao? DataOutputStream sẽ không giúp bạn ở đó.
dùng1132959

4
Nếu anh ta thực hiện đa ngôn ngữ hoặc đa nền tảng, thì việc gửi các byte theo thứ tự đã biết là rất quan trọng. Phương pháp này thực hiện điều đó (nó viết chúng "byte cao trước") theo các tài liệu. Câu trả lời được chấp nhận không (nó viết chúng theo "thứ tự hiện tại" theo các tài liệu). Câu hỏi nói rằng anh ta muốn gửi chúng qua kết nối TCP. Đây byte[]chỉ là một phương tiện để kết thúc đó.
Ian McLaird

3
@IanMcLaird Câu trả lời được chấp nhận không sử dụng một đơn đặt hàng đã biết. Nó tạo ra một cái mới ByteBuffermà theo các tài liệu "Thứ tự ban đầu của bộ đệm byte luôn là BIG_ENDIAN.
David Phillips

4

Chỉ cần viết dài vào DataOutputStream với ByteArrayOutputStream nằm bên dưới . Từ ByteArrayOutputStream, bạn có thể lấy mảng byte thông qua 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));       
        }
}

Hoạt động cho các nguyên thủy khác cho phù hợp.

Gợi ý: Đối với TCP, bạn không cần byte [] theo cách thủ công. Bạn sẽ sử dụng một socket socket và các luồng của nó

OutputStream os=socket.getOutputStream(); 
DataOutputStream dos=new DataOutputStream(os);
dos.writeLong(l);
//etc ..

thay thế.


4

Bạn có thể sử dụng triển khai trong org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html

Mã nguồn ở đây:

http://grepcode.com/file/reposective.cloudera.com/content/reposeocate/release/com.cloudera.hbase/hbase/0.89.20100924-28/org/apache/hadoop/hbase/util/Bytes.java# Byte.toBytes% 28long% 29

Tìm kiếm các phương thức toLong và toBytes.

Tôi tin rằng giấy phép phần mềm cho phép bạn lấy các phần của mã và sử dụng nó nhưng vui lòng xác minh điều đó.


Tại sao việc triển khai đó sử dụng XOR (^ =) thay vì OR? github.com/apache/hbase/blob/master/hbase-common/src/main/java/
Kẻ

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
bạn có thể bỏ qua ArrayList: byte tĩnh công khai [] longToBytes (long l) {long num = l; byte [] byte = byte mới [8]; for (int i = byte.length - 1, i> = 0; i--) {byte [i] = (byte) (num & 0xff); số >> = 8; } trả lại bytesp; }
eckes

Phương thức ban đầu không hoạt động với các số âm vì nó có một vòng lặp vô hạn trong khi (l! = 0), phương thức của @ eckes không hoạt động với các số trên 127 vì anh ta không tính đến các byte bị âm trên 127 nguyên nhân họ đã ký
Big_Bad_E

2

Tôi sẽ thêm một câu trả lời khác nhanh nhất có thể (vâng, thậm chí nhiều hơn câu trả lời được chấp nhận), NHƯNG nó sẽ không hoạt động cho mọi trường hợp. TUY NHIÊN, nó S work hoạt động cho mọi kịch bản có thể tưởng tượng được:

Bạn chỉ có thể sử dụng String làm trung gian. Lưu ý, điều này S give cung cấp cho bạn kết quả chính xác mặc dù có vẻ như việc sử dụng Chuỗi có thể mang lại kết quả sai NHƯ VẬY NHƯ BẠN BIẾT BẠN ĐANG LÀM VIỆC VỚI CÁC CHIẾN LƯỢC "BÌNH THƯỜNG". Đây là một phương pháp để tăng hiệu quả và làm cho mã đơn giản hơn, đổi lại phải sử dụng một số giả định trên chuỗi dữ liệu mà nó hoạt động.

Sử dụng phương pháp này: Nếu bạn đang làm việc với một số ký tự ASCII như các ký hiệu này ở đầu bảng ASCII, các dòng sau có thể thất bại, nhưng hãy đối mặt với nó - có thể bạn sẽ không bao giờ sử dụng chúng.

Pro sử dụng phương pháp này: Hãy nhớ rằng hầu hết mọi người thường làm việc với một số chuỗi bình thường mà không có bất kỳ ký tự bất thường nào và sau đó phương pháp là cách đơn giản nhất và nhanh nhất để đi.

từ Dài đến byte []:

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

từ byte [] đến Long:

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

2
Xin lỗi vì đã hoại tử, nhưng đó chỉ là sai. Ví dụ. Chuỗi.valueOf (0) .getBytes () [0] == 0x30. Sự ngạc nhiên! Chuỗi # getBytes sẽ trả về các ký hiệu chữ số được mã hóa ASCII, không phải các chữ số: '0'! = 0, nhưng '0' == 0x30
Kirill Gamazkov

Có lẽ nếu bạn đã đọc toàn bộ câu trả lời của tôi thì bạn sẽ thấy tôi đã cảnh báo về nó trong chính câu trả lời ..
Yonatan Nir

1
Ah, tôi đã bỏ lỡ điểm rằng dữ liệu byte [] trung gian được coi là (gần như) mờ. Thủ thuật của bạn sẽ làm cho kịch bản này.
Kirill Gamazkov 17/03/2017

1

Nếu bạn đã sử dụng một OutputStreamđể ghi vào ổ cắm, thì DataOutputStream có thể phù hợp. Đây là một ví dụ:

// Assumes you are currently working with a SocketOutputStream.

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

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

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

Có nhiều phương pháp tương tự cho short, int, floatvv Sau đó, bạn có thể sử dụng DataInputStream ở phía bên nhận.



0

Đây là một cách khác để chuyển đổi byte[]sang longsử dụng Java 8 hoặc mới hơn:

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

Chuyển đổi a longcó thể được biểu thị dưới dạng các bit bậc cao và bậc thấp của hai giá trị nguyên theo bit-OR. Lưu ý rằng đó toUnsignedLonglà từ Integerlớp và cuộc gọi đầu tiên toUnsignedLongcó thể là thừa.

Việc chuyển đổi ngược lại cũng có thể được kiểm soát, như những người khác đã đề cập.


0

Tiện ích mở rộng Kotlin cho các loại Long và ByteArray:

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

Bạn có thể xem mã đầy đủ trong thư viện của tôi https://github.com/ArtemBotnev/low-level-extensions

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.