Как мне конвертировать Long в byte [] и обратно в Java

159

Как я могу преобразовать longв byte[]и обратно в Java?

Я пытаюсь преобразовать longв byte[]так, чтобы я смог отправить byte[]через TCP-соединение. С другой стороны, я хочу взять это byte[]и преобразовать обратно в double.

Emre801
источник
Другой альтернативой будет Maps.transformValues, общий инструмент для преобразования коллекций. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Рауль
1
См. Также stackoverflow.com/q/27559449/32453, если ваша цель - преобразовать long в наименьшее количество символов Base64.
rogerdpack
Возможно, следует подчеркнуть, что конвейер преобразования имеет вид «long -> byte [] -> double», а не «long -> byte [] -> long -> double».
Кирилл Гамазков

Ответы:

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 для каждого преобразования. Не хорошо, если вы отправляете несколько длинных на одно сообщение и / или много сообщений.
Стивен С
1
@Stephen - я просто делал достаточно, чтобы продемонстрировать, как использовать ByteBuffer, но я пошел дальше и добавил пример использования его в служебном классе.
Брэд Мейс
8
Я думаю, что bytesToLong () здесь потерпит неудачу, так как позиция после пут находится в конце буфера, а не в начале. Я думаю, что вы получите исключение переполнения буфера.
Алекс Миллер
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;
}
Wytze
источник
1
Вместо результата | = (b [i] & 0xFF); Мы могли бы просто использовать результат | = b [i]; так как и с 0xFF немного ничего не меняет.
Випул
3
@Vipul Побитовое-и имеет значение, потому что при выполнении только result |= b[i]байтовое значение будет сначала преобразовано в длинное, которое подписывает расширение. Байт со значением -128 (шестнадцатеричный 0x80) превратится в длинный со значением -128 (шестнадцатеричный 0xFFFF FFFF FFFF FF80). Сначала после преобразования находятся значения или: вместе. Использование побитовых и защищает от этого сначала преобразовать байты в междунар и отрезав знаковое расширение: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Почему байты подписываются в Java, для меня немного загадка, но я думаю, что это подходит для других типов.
Мозговой штурм
@Brainstorm Я пробовал вариант -128 с | = b [i] и с | = (b [i] & 0xFF), и результаты совпадают !!
Махмуд Ханафи
Проблема в том, что байт повышается до применения сдвига, что вызывает странные проблемы со знаковым битом. Поэтому мы сначала и (&) используем 0xFF, чтобы получить правильное значение для смещения.
Wytze
Я пытаюсь преобразовать эти данные (data = new byte [] {(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8};) в long, но он возвращает) ложное значение -2619032330856274424, ожидаемое значение 989231983928329832
Джифри Джеки,
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
Он спросил, как вы конвертируете в байты [] и обратно. Хороший ответ, но не ответил на вопрос. Вы спрашиваете почему, потому что вы полагаете, что это не нужно, но это неверное предположение. Что, если он занимается кросс-языком или кроссплатформенностью? DataOutputStream не поможет вам там.
user1132959
4
Если он работает на разных языках или на разных платформах, то отправка байтов в известном порядке важна. Этот метод делает это (он пишет их «старший байт первым») в соответствии с документацией. Принятый ответ не (он записывает их в «текущем порядке» в соответствии с документами). В вопросе говорится, что он хочет отправить их по TCP-соединению. Это byte[]просто средство для достижения этой цели.
Ян Маклаирд
3
@IanMcLaird Принятый ответ использует известный порядок. Он создает новый, ByteBufferкоторый в соответствии с документами "Первоначальный порядок байтового буфера всегда BIG_ENDIAN.
Дэвид Филлипс
4

Просто запишите long в 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 и его потоки

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.

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

Marquez
источник
Почему эта реализация использует XOR (^ =) вместо OR? 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;
    }
Maziar
источник
2
вы можете пропустить ArrayList: public static byte [] longToBytes (long l) {long num = l; byte [] bytes = новый байт [8]; for (int i = bytes.length - 1, i> = 0; i--) {bytes [i] = (byte) (num & 0xff); число >> = 8; } return bytesp; }
eckes
Исходный метод не работает с отрицательными числами, так как он входит в бесконечный цикл в то время как (l! = 0), метод @ eckes не работает с числами свыше 127, потому что он не учитывает отрицательные значения байтов за 127 они подписаны.
Big_Bad_E
2

Я добавлю другой ответ, который является самым быстрым из возможных possible (да, даже больше, чем принятый ответ), НО он не будет работать для каждого отдельного случая. ОДНАКО, это будет работать для каждого мыслимого сценария:

Вы можете просто использовать String в качестве промежуточного звена. Обратите внимание, что это даст вам правильный результат, даже если кажется, что использование String может привести к неверным результатам, поскольку вы знаете, что работаете с «нормальными» строками. Это метод повышения эффективности и упрощения кода, который, в свою очередь, должен использовать некоторые допущения в отношении строк данных, с которыми он работает.

Против использования этого метода: если вы работаете с некоторыми символами ASCII, такими как эти символы, в начале таблицы ASCII, следующие строки могут не работать, но давайте посмотрим правде в глаза - вы, вероятно, никогда не будете их использовать.

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

от длинного к байтовому []:

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

от байта [] к длинному:

long longVar = Long.valueOf(new String(byteArr)).longValue();
Йонатан Нир
источник
2
Извините за некропостинг, но это просто неправильно. Например 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));
}

Преобразование 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

Артем Ботнев
источник