Як конвертувати Long в байт [] і назад у Java


159

Як перетворити longдо byte[]і назад в Java?

Я намагаюся перетворити a longв byte[]так, що я зможу надіслати byte[]через TCP-з'єднання. З іншого боку, я хочу взяти це byte[]і перетворити його назад в double.


Ще одна альтернатива - Maps.transformValues, загальний інструмент для перетворення колекцій. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul

1
Дивіться також stackoverflow.com/q/27559449/32453, якщо ваша мета - перетворити довгу в найменшу кількість Base64 символів.
rogerdpack

Можливо, слід підкреслити, що конвеєр конверсії - "довгий -> байт [] -> подвійний", а не "довгий -> байт [] -> довгий -> подвійний".
Кирило Гамазков

Відповіді:


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
Розумний ... але ви створюєте та скасовуєте тимчасовий байт-буфер для кожної конверсії. Непогано, якщо ви надсилаєте кілька повідомлень за повідомлення та / або багато повідомлень.
Стівен С

1
@Stephen - Я просто робив достатньо для того, щоб продемонструвати, як користуватися ByteBuffer, але я пішов вперед і додав приклад використання його в клас утиліти.
Бред Мейс

8
Я думаю, що тут bytesToLong () не вдасться, оскільки позиція після того, як put знаходиться в кінці буфера, а не на початку. Я думаю, ви отримаєте виняток під буфер.
Алекс Міллер

11
Попередньо Java 8 ви можете використовувати Long.SIZE / Byte.SIZE замість Long.BYTES, щоб уникнути магічного числа.
jvdbogae

8
Повторне використання цього байт-буфера є дуже проблематичним, і не лише з міркувань безпеки потоку, як коментують інші. Мало того, що між ними знадобиться ".clear ()", але й не очевидно, що виклик .array () на ByteBuffer повертає резервний масив проти копії. Таким чином, якщо ви зателефонували неодноразово і утримували інші результати, вони насправді є все тим самим масивом, який кілька разів перезаписує попередні значення. Посилання 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 трохи не змінює нічого.
Випул

3
@Vipul Побітове значення має значення, тому що, виконуючи лише result |= b[i]значення байта, спочатку буде перетворено на довге, що робить розширення знаку. Байт зі значенням -128 (шістнадцятковий 0x80) перетвориться на довгий зі значенням -128 (шістнадцятковий 0xFFFF FFFF FFFF FF80). Першими після перетворення є значення або: ed разом. Використання побітових і захищає від цього спочатку перетворити байти в міжнародному і відрізавши знакове розширення: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Чому байти підписані в Java, для мене трохи таємниця, але я думаю, що це вписується в інші типи.
Мозковий штурм

@Brainstorm Я спробував випадок -128 з | = b [i] і з | = (b [i] & 0xFF), і результати однакові !!
Махмуд Ханафій

Проблема полягає в тому, що байт підвищується до того, як буде застосовано зсув, що викликає дивні проблеми з бітом знака. Тому ми спочатку і (&) це з 0xFF, щоб отримати правильне значення для зміщення.
Wytze

Я намагаюся перетворити ці дані (дані = новий байт [] {(байт) 0xDB, (байт) 0xA7, 0x53, (байт) 0xF8, (байт) 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);

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 , останній повинен враховувати нульові значення.

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 вам там не допоможе.
користувач1132959

4
Якщо він робить міжмовні або крос-платформи, то важлива важлива відправка байтів у відомому порядку. Цей метод робить це (він записує їх "першим байтом") згідно з документами. Прийнятої відповіді немає (він пише їх у "поточному порядку" згідно з документами). У запитанні зазначено, що він хоче відправити їх через з'єднання TCP. Це byte[]лише засіб для досягнення цієї мети.
Ian McLaird

3
@IanMcLaird У прийнятій відповіді використовується відомий порядок. Він створює нове, ByteBufferяке згідно з документами "Початковий порядок байтного буфера завжди BIG_ENDIAN.
Девід Філліпс

4

Просто запишіть довгу в DataOutputStream із базовим ByteArrayOutputStream . З 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 вам не потрібен байт [] вручну. Ви будете використовувати 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.

Я вважаю, що ліцензія на програмне забезпечення дозволяє вам брати частини коду та використовувати його, але будь ласка, переконайтесь у цьому.


Чому ця реалізація використовує XOR (^ =) замість АБО? github.com/apache/hbase/blob/master/hbase-common/src/main/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
ви можете пропустити ArrayList: public static byte [] longToBytes (long l) {long num = l; байт [] байт = новий байт [8]; for (int i = bytes.length - 1, i> = 0; i--) {байт [i] = (байт) (num & 0xff); число >> = 8; } повернути пропускний шлях; }
eckes

Оригінальний метод не працює з негативними числами, оскільки він потрапляє у нескінченний цикл, поки (l! = 0), метод @ eckes не працює з числами понад 127, оскільки він не враховує, що байти переходять у негативне значення над 127 причиною вони підписані.
Big_Bad_E

2

Я додам ще одну відповідь, яка є найшвидшою з можливих ׂ (так, навіть більше, ніж прийнята відповідь), Але вона не працюватиме для кожного окремого випадку. ЗНАЙДІ, це буде працювати для кожного можливого сценарію:

Ви можете просто використовувати String як проміжний. Зауважте, це дасть вам правильний результат, навіть якщо здається, що використання String може призвести до неправильних результатів, КАК ДОВІГО, ЯК ВИ ЗНАЄТИ, ЩО ВИ РОБОТИ З "НОРМАЛЬНИМИ" РУКАМИ. Це метод підвищення ефективності та спрощення коду, який взамін повинен використовувати деякі припущення щодо рядків даних, на яких він працює.

Що стосується використання цього методу: Якщо ви працюєте з деякими символами ASCII, такими як ці символи, на початку таблиці ASCII, наступні рядки можуть вийти з ладу, але визнаймо це - ви, ймовірно, ніколи їх не будете використовувати.

Про використання цього методу. Пам’ятайте, що більшість людей зазвичай працюють із звичайними рядками без будь-яких незвичних символів, і тоді метод є найпростішим і швидким способом.

від Long to byte []:

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

від байти [] до Довго:

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

2
Вибачте за некропостинг, але це просто неправильно. E. g. String.valueOf (0) .getBytes () [0] == 0x30. Сюрприз! Рядок # getBytes поверне символи, кодовані ASCII, а не цифри: '0'! = 0, але '0' == 0x30
Кирило Гамазков

Ну, можливо, якби ви прочитали всю мою відповідь, то ви побачили, що я попереджав про це у самій відповіді ..
Йонатан Нір

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може бути виражене як біти високого та низького порядку двох цілих значень, що підлягають побітовому АБО. Зауважте, що toUnsignedLongце від Integerкласу, і перший дзвінок до toUnsignedLongможе бути зайвим.

Протилежне перетворення також може бути розкрученим, як уже згадували інші.


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.