ฉันจะแปลง Long เป็น byte [] และ back in java ได้อย่างไร


159

ฉันจะแปลงlonga byte[]และกลับเป็น Java ได้อย่างไร

ฉันพยายามแปลง a เป็นlonga byte[]เพื่อที่ฉันจะสามารถส่งbyte[]ผ่านการเชื่อมต่อ TCP ในอีกด้านหนึ่งผมอยากจะใช้เวลาที่และแปลงกลับเป็นbyte[]double


อีกทางเลือกหนึ่งคือ Maps.transformValues ​​ซึ่งเป็นเครื่องมือทั่วไปสำหรับการแปลงคอลเล็กชัน docs.guava-lafes.googlecode.com/git-history/release/javadoc/…
Raul

1
ดูstackoverflow.com/q/27559449/32453 ด้วยหากเป้าหมายของคุณคือการแปลงความยาวเป็นจำนวนน้อยที่สุดของ Base64 chars
rogerdpack

บางทีควรเน้นว่าไปป์ไลน์การแปลงคือ 'long -> byte [] -> double' ไม่ใช่ 'long -> byte [] -> long -> double'
Kirill Gamazkov

คำตอบ:


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 ในกรณีส่วนใหญ่ และหากคุณมีข้อคัดค้านที่แปลกประหลาดกับห้องสมุดคุณควรพิจารณาคำตอบนี้ก่อนสำหรับวิธีแก้ปัญหาภาษาจาวา ฉันคิดว่าสิ่งสำคัญที่คำตอบของฉันเป็นจริงคือคุณไม่ต้องกังวลเกี่ยวกับ endian-ness ของระบบด้วยตัวเอง


3
ฉลาด ... แต่คุณสร้างและละทิ้ง ByteBuffer ชั่วคราวสำหรับการแปลงแต่ละครั้ง ไม่ดีถ้าคุณส่งข้อความยาวหลายต่อข้อความและ / หรือส่งข้อความจำนวนมาก
สตีเฟ่นซี

1
@ สตีเฟ่น - ฉันแค่ทำพอที่จะสาธิตวิธีการใช้ ByteBuffer แต่ฉันไปข้างหน้าและเพิ่มตัวอย่างของการใช้ในคลาสยูทิลิตี้
Brad Mace

8
ฉันคิดว่า bytesToLong () ที่นี่จะล้มเหลวเนื่องจากตำแหน่งหลังจากวางอยู่ที่ท้ายบัฟเฟอร์ไม่ใช่จุดเริ่มต้น ฉันคิดว่าคุณจะได้รับข้อยกเว้นบัฟเฟอร์อันเดอร์โฟล์
Alex Miller

11
Pre-Java 8 คุณสามารถใช้ Long.SIZE / Byte.SIZE แทน Long.BYTES เพื่อหลีกเลี่ยงหมายเลขเวทย์มนตร์
jvdbogae

8
การใช้ bytebuffer นั้นใหม่นั้นมีปัญหาอย่างมากและไม่เพียง แต่เพื่อความปลอดภัยของเธรดเท่านั้นตามที่คนอื่น ๆ ให้ความเห็น ไม่เพียง แต่จะต้อง 'ชัดเจน ()' ในระหว่างนั้น แต่สิ่งที่ไม่ชัดเจนก็คือการเรียก. array () บน ByteBuffer กำลังส่งคืนอาร์เรย์สำรองกับสำเนา ดังนั้นหากคุณโทรซ้ำ ๆ กันและค้างไว้ที่ผลลัพธ์อื่น ๆ จริงๆแล้วอาร์เรย์ทั้งหมดเดียวกันทั้งหมดที่ซ้ำ ๆ กันจะเขียนทับค่าก่อนหน้า ลิงค์ hadoop ในความคิดเห็นด้านล่างเป็นสิ่งที่มีประสิทธิภาพมากที่สุดและหลีกเลี่ยงปัญหาใด ๆ เหล่านี้
Aaron Zinman


84

ฉันทดสอบเมธอด ByteBuffer จากการใช้งาน bitwise ธรรมดา แต่วิธีหลังนั้นเร็วกว่ามาก

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]ค่าไบต์จะถูกแปลงเป็นความยาวซึ่งจะขยายส่วนขยายของสัญญาณ ไบต์ที่มีค่า -128 (ฐานสิบหก0x80) จะเปลี่ยนเป็นความยาวที่มีค่า -128 (ฐานสิบหก0xFFFF FFFF FFFF FF80) ก่อนอื่นหลังจากการแปลงคือค่าหรือ: ed เข้าด้วยกัน โดยใช้ค่าที่เหมาะสมและป้องกันการนี้โดยแปลงแรกไบต์เป็น int (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80และตัดออกจากการขยายเข้าสู่ระบบ: ทำไมไบต์ที่ลงชื่อเข้าใช้จาวาเป็นเรื่องลึกลับสำหรับฉัน แต่ฉันคิดว่ามันเหมาะกับประเภทอื่น ๆ
ระดมสมอง

@Brainstorm ฉันลองใช้ -128 กับ | = b [i] และ | | ((b [i] & 0xFF) และผลลัพธ์เหมือนกัน !!
Mahmoud Hanafy

ปัญหาคือว่าไบต์ที่ได้รับการเลื่อนตำแหน่งก่อนที่จะใช้การเปลี่ยนซึ่งทำให้เกิดปัญหาแปลก ๆ กับบิตสัญญาณ ดังนั้นเราก่อนและ (&) ด้วย 0xFF เพื่อให้ได้ค่าที่ถูกต้องในการเลื่อน
Wytze

ฉันพยายามแปลงข้อมูลนี้ (data = new byte [] {(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8}; ค่าเท็จ -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);

ยาว -> ไบต์ []เป็นคู่ที่แน่นอนกับข้างต้น

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

ทำไมคุณต้องมีไบต์ [] ทำไมไม่เขียนลงในซ็อกเก็ต?

ฉันถือว่าคุณหมายถึงความยาวมากกว่ายาวความหลังจำเป็นต้องยอมให้มีค่าว่าง

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

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

8
เขาถามว่าคุณแปลงเป็นไบต์ [] และย้อนกลับได้อย่างไร คำตอบที่ดี แต่ไม่ได้ตอบคำถาม คุณถามว่าทำไมเพราะคุณคิดว่ามันไม่จำเป็น แต่นั่นเป็นข้อสันนิษฐานที่ผิด ถ้าเขาทำข้ามภาษาหรือข้ามแพลตฟอร์มล่ะ? DataOutputStream จะไม่ช่วยคุณที่นั่น
user1132959

4
หากเขาทำข้ามภาษาหรือข้ามแพลตฟอร์มการส่งไบต์ตามลำดับที่ทราบนั้นเป็นสิ่งสำคัญ วิธีนี้ใช้วิธีนี้ (เขียนว่า "high byte first") ตามเอกสาร คำตอบที่ยอมรับไม่ได้ (มันเขียนไว้ใน "ลำดับปัจจุบัน" ตามเอกสาร) คำถามระบุว่าเขาต้องการส่งพวกเขาผ่านการเชื่อมต่อ TCP byte[]เป็นเพียงหมายถึงการสิ้นสุดที่
Ian McLaird

3
@IanMcLaird คำตอบที่ยอมรับจะใช้คำสั่งที่รู้จัก มันสร้างสิ่งใหม่ByteBufferตามเอกสาร"ลำดับเริ่มต้นของบัฟเฟอร์ไบต์มักจะเป็น BIG_ENDIAN
David Phillips

4

เพียงแค่เขียนยาวไปDataOutputStreamกับพื้นฐานByteArrayOutputStream จาก ByteArrayOutputStream คุณสามารถรับ byte-array ผ่าน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 คุณไม่จำเป็นต้องใช้ไบต์ [] ด้วยตนเอง คุณจะใช้ซ็อกเก็ต 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

ฉันเชื่อว่าใบอนุญาตซอฟต์แวร์อนุญาตให้คุณใช้ส่วนหนึ่งของรหัสและใช้งานได้ แต่โปรดยืนยันว่า


ทำไมการติดตั้งใช้ XOR (^ =) แทนที่จะใช้ OR github.com/apache/hbase/blob/master/hbase-common/src/main/java/
......

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; ไบต์ [] ไบต์ = ไบต์ใหม่ [8]; สำหรับ (int i = bytes.length - 1, i> = 0; i--) {ไบต์ [i] = (ไบต์) (num & 0xff); NUM >> = 8; } กลับ bytesp; }
ปิด

วิธีการดั้งเดิมไม่ทำงานกับตัวเลขลบเนื่องจากวนซ้ำไม่สิ้นสุดในขณะที่ (l! = 0) วิธีการของ @ eckes ไม่ทำงานกับตัวเลขที่มากกว่า 127 เพราะเขาไม่ได้คิดว่าไบต์จะเป็นลบมากกว่า 127 สาเหตุ พวกเขาเซ็น
Big_Bad_E

2

ฉันจะเพิ่มคำตอบอีกข้อหนึ่งซึ่งเป็นคำตอบที่เร็วที่สุดׂ (ใช่ยิ่งกว่าคำตอบที่ยอมรับ) แต่จะไม่สามารถใช้ได้กับทุกกรณี อย่างไรก็ตามมันจะใช้ได้กับทุกสถานการณ์ที่เป็นไปได้:

คุณสามารถใช้สตริงเป็นสื่อกลางได้ โปรดทราบว่าสิ่งนี้จะให้ผลลัพธ์ที่ถูกต้องแก่คุณแม้ว่าดูเหมือนว่าการใช้ String อาจให้ผลลัพธ์ที่ไม่ถูกต้องตราบใดที่คุณรู้ว่าคุณกำลังทำงานกับสตริง "ปกติ" นี่เป็นวิธีการที่จะเพิ่มประสิทธิภาพและทำให้รหัสง่ายขึ้นซึ่งในทางกลับกันจะต้องใช้สมมติฐานบางอย่างกับสายข้อมูลที่ทำงานอยู่

ข้อเสียของการใช้วิธีนี้:หากคุณกำลังทำงานกับอักขระ ASCII บางตัวเช่นสัญลักษณ์เหล่านี้ในตอนต้นของตาราง ASCII บรรทัดต่อไปนี้อาจล้มเหลว แต่ลองมาดูกัน - คุณอาจไม่เคยใช้เลย

Pro ของการใช้วิธีนี้:จำไว้ว่าคนส่วนใหญ่มักจะทำงานกับสายปกติโดยไม่ต้องมีตัวอักษรผิดปกติใด ๆ แล้ววิธีนี้เป็นวิธีที่ง่ายและรวดเร็วที่สุดในการไป

จากยาวเป็นไบต์ []:

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

จากไบต์ [] ถึงยาว:

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 Nir

1
อาฉันพลาดจุดที่ข้อมูลกลางไบต์ [] ถูกมองว่าเป็นทึบ (เกือบ) เคล็ดลับของคุณจะทำสำหรับสถานการณ์นี้
คิริลล์แกมมาคอฟ

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

มีวิธีการที่คล้ายกันสำหรับเป็นshort, int, floatฯลฯ จากนั้นคุณสามารถใช้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สามารถแสดงเป็นบิตสูงและต่ำตามลำดับของค่าจำนวนเต็มสองค่าซึ่งขึ้นอยู่กับ bitwise-OR โปรดทราบว่าtoUnsignedLongมาจากIntegerชั้นเรียนและสายแรกที่toUnsignedLongอาจจะฟุ่มเฟือย

การแปลงตรงกันข้ามสามารถ unrolled เช่นกันตามที่คนอื่น ๆ ได้กล่าวถึง


0

ส่วนขยาย Kotlin สำหรับประเภท Long และ 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()
}

คุณสามารถดูรหัสเต็มในห้องสมุดของฉันhttps://github.com/ArtemBotnev/low-level-extensions

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.