Java'da Long'u bayta [] ve geri nasıl dönüştürebilirim


159

Nasıl dönüştürebilirim longbir etmek byte[]Java ve arka?

Ben bir TCP bağlantısı üzerinden göndermek mümkün olacak longbir dönüştürmek için çalışıyorum . Diğer tarafta bunu alıp tekrar a dönüştürmek istiyorum .byte[]byte[]byte[]double


Başka bir alternatif, koleksiyonları dönüştürmek için genel bir araç olan Maps.transformValues ​​olacaktır. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul

1
Hedefiniz uzun bir süreyi en az sayıda Base64 karakterine dönüştürmekse stackoverflow.com/q/27559449/32453 adresine bakın .
rogerdpack

Belki dönüşüm hattının 'uzun -> bayt [] -> çift' değil, 'uzun -> bayt [] -> uzun -> çift' olduğu vurgulanmalıdır.
Kirill Gamazkov

Yanıtlar:


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

Veya art arda ByteBuffers oluşturmaktan kaçınmak için bir sınıfa sarılır:

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

Bu çok popüler hale geldiğinden, vakaların büyük çoğunluğunda Guava gibi bir kütüphane kullanmaktan daha iyi olduğunu düşünüyorum. Kütüphanelere garip bir karşıtlık varsa, muhtemelen bu cevabı yerel java çözümleri için düşünmelisiniz . Bence cevabımın asıl ilgisi bu, sistemin endeksiliği hakkında endişelenmenize gerek olmamasıdır.


3
Zeki ... ancak her dönüşüm için geçici bir ByteBuffer oluşturup atarsınız. Mesaj başına birden fazla uzun mesaj ve / veya çok sayıda mesaj gönderiyorsanız iyi değildir.
Stephen C

1
@Stephen - Sadece ByteBuffer'ın nasıl kullanılacağını göstermek için yeterli yapıyordum, ancak devam ettim ve bir yardımcı program sınıfında kullanmanın bir örneğini ekledim.
Brad Mace

8
Bence bytesToLong (), putdan sonraki konumun başında değil tamponun sonunda olduğu için başarısız olur. Bence arabellek yetersizliği istisnası olur.
Alex Miller

11
Java 8 öncesi, sihirli bir sayıyı önlemek için Long.BYTES yerine Long.SIZE / Byte.SIZE kullanabilirsiniz.
jvdbogae

8
Bu bytebuffer'ın yeniden kullanımı, sadece diğerlerinin açıkladığı gibi iplik güvenliği nedeniyle değil, son derece sorunludur. Arada sadece bir .clear () 'ye ihtiyaç duyulmakla kalmaz, aynı zamanda ByteBuffer'da .array () çağrılması destek dizisini bir kopyaya geri döndürüyor. Böylece, tekrar tekrar çağırır ve diğer sonuçlara basılı tutarsanız, aslında hepsi tekrar tekrar önceki değerlerin üzerine yazan dizidir. Aşağıdaki yorumda yer alan hadoop bağlantısı en yüksek performansa sahiptir ve bu sorunlardan herhangi birini önler.
Aaron Zinman


84

ByteBuffer yöntemini düz bitsel işlemlere karşı test ettim ancak ikincisi önemli ölçüde daha hızlı.

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
Sonuç yerine | = (b [i] & 0xFF); Sonuç | = b [i]; gibi ve 0xFF ile biraz hiçbir şey değiştirmez.
Vipul

3
@Vipul Bitwise-ve önemli çünkü sadece result |= b[i]bayt değeri yapılırken uzun olana dönüştürülür ve bu da uzantıyı imzalar. -128 (hex 0x80) değerine sahip bir bayt, -128 (hex ) değerine sahip bir uzun haline dönüşür 0xFFFF FFFF FFFF FF80. Dönüşümden sonraki değerler veya: birlikte düzenlenir. Bitsel-ve Kullanma ilk işareti uzantısı kapalı int ve kesim için bayt dönüştürerek buna karşı korur: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Java'da baytların neden imzalandığı benim için biraz gizem, ama sanırım diğer türlere de uyuyor.
Beyin Fırtınası

@Brainstorm | = b [i] ve | = (b [i] & 0xFF) ile durum -128'i denedim ve sonuçlar aynı !!
Mahmoud Hanafy

Sorun bayt kaydırma uygulanmadan önce yükseltilmesi işaret biti ile garip sorunlara neden olmasıdır. Bu nedenle ilk olarak ve doğru değere kaydırmak için 0xFF ile (&) kullanıyoruz.
Wytze

Bu verileri (veri = yeni bayt [] {(bayt) 0xDB, (bayt) 0xA7, 0x53, (bayt) 0xF8, (bayt) 0xA8, 0x0C, 0x66, 0x8};) uzun süreye dönüştürmeye çalışıyorum, ancak geri dönüyor yanlış değer -2619032330856274424, beklenen değer 989231983928329832
jefry jacky

29

Hızlı bir kaydedilmemiş sürüm arıyorsanız, bu, "b" adlı bir bayt dizisinin 8 uzunluğunda olduğu varsayılarak hile yapmalıdır:

bayt [] -> uzun

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 [] yukarıdakinin tam karşılığı olarak

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
Teşekkür ederim, en iyisi!
Miha_x64

15

Neden bayt [] 'a ihtiyacınız var? neden sadece sokete yazmıyorsunuz?

Ne demek farz uzun ziyade Long , ikincisi ihtiyaçları boş değerlere izin verme.

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

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

8
Bayt [] ve arkasına nasıl dönüştüğünüzü sordu. İyi cevap ama soruya cevap vermedi. Nedenini soruyorsunuz çünkü bunun gereksiz olduğunu varsayıyorsunuz ama bu yanlış bir varsayım. Ya dil arası ya da çapraz platform yapıyorsa? DataOutputStream orada size yardımcı olmaz.
user1132959

4
Çapraz dil veya çapraz platform yapıyorsa, baytları bilinen bir sırayla göndermek önemlidir. Bu yöntem, dokümanlara göre bunu yapar (önce "yüksek bayt" yazar). Kabul edilen cevap (dokümana göre “mevcut sıraya” yazar). Soru, onları bir TCP bağlantısı üzerinden göndermek istediğini belirtiyor. Bu byte[]sadece bunun için bir araçtır.
Ian McLaird

3
@IanMcLaird Kabul edilen cevap bilinen bir sipariş kullanıyor. Bu yeni bir oluşturur ByteBufferdokümanlar göre hangi "bir bayt tampon ilk sipariş her zaman BIG_ENDIAN olduğunu.
David Phillips

4

Sadece uzun yazmak DataOutputStream altta yatan ile ByteArrayOutputStream . ByteArrayOutputStream öğesinden, byte dizisini toByteArray () üzerinden alabilirsiniz :

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

Buna göre diğer ilkel ürünler için de çalışır.

İpucu: TCP için bayt [] 'a manuel olarak ihtiyacınız yoktur. Bir Soket socket ve akarsuları kullanacaksınız

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

yerine.


4

Uygulamayı org.apache.hadoop.hbase.util.Bytes adresinde kullanabilirsiniz http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html

Kaynak kodu burada:

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 ve toBytes yöntemlerini arayın.

Yazılım lisansının kodun bir bölümünü alıp kullanmanıza izin verdiğine inanıyorum, ancak lütfen bunu doğrulayın.


Bu uygulama neden OR yerine XOR (^ =) kullanıyor? 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'i atlayabilirsiniz: public static byte [] longToBytes (long l) {long num = l; bayt [] bayt = yeni bayt [8]; (int i = bytes.length - 1 için, i> = 0; i--) {bayt [i] = (bayt) (num & 0xff); num >> = 8; } dönüş bayt; }
eckes

Orijinal yöntem sonsuz sayılarla çalışmadığı için negatif sayılarla çalışmaz (l! = 0), @ eckes'in yöntemi 127'nin üzerindeki sayılarla çalışmaz çünkü 127'den fazla negatif giden baytları hesaba katmaz imzalanmışlar.
Big_Bad_E

2

Mümkün olan en hızlı olan başka bir cevap ekleyeceğim ׂ (evet, kabul edilen cevaptan daha fazla), AMA her bir vaka için işe yaramayacak. ANCAK, akla gelebilecek her senaryo için çalışacaktır:

String'i ara olarak kullanabilirsiniz. Not, "NORMAL" STRINGS İLE ÇALIŞTIĞINIZI BİLDİRDİĞİNİZ GİBİ yanlış sonuçlar verebilir gibi görünse de, bu size doğru sonucu verecektir. Bu, etkinliği artırmak ve kodu daha basit hale getirmek için bir yöntemdir ve bunun karşılığında üzerinde çalıştığı veri dizelerinde bazı varsayımlar kullanması gerekir.

Con bu yöntemi kullanarak: ASCII tablosunun başlangıcında bu semboller gibi bazı ASCII karakterleriyle çalışıyorsanız, aşağıdaki satırlar başarısız olabilir, ancak bununla yüzleşelim - muhtemelen bunları asla kullanmayacaksınız.

Bu yöntemi kullanma yanlısı: Çoğu insanın olağandışı karakterleri olmayan bazı normal dizelerle çalıştığını ve ardından yöntemin en basit ve en hızlı yol olduğunu unutmayın.

Uzuntan bayta []:

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

bayttan [] uzununa:

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

2
Nekropostlama için özür dilerim, ama bu sadece yanlış. Örneğin. String.valueOf (0) .getBytes () [0] == 0x30. Sürpriz! Dize # getBytes basamaklara değil, ASCII kodlu basamak sembollerini döndürür: '0'! = 0, ancak '0' == 0x30
Kirill Gamazkov

Belki de cevabımın tamamını okuduysanız, cevabın kendisinde bu konuda uyarmış olduğumu görmüştünüz ..
Yonatan Nir

1
Ah, ara bayt [] verilerinin (neredeyse) opak olarak değerlendirildiği noktasını kaçırdım. Bu senaryo için hileniz yapacak.
Kirill Gamazkov

1

OutputStreamSokete yazmak için zaten bir kullanıyorsanız , DataOutputStream uygun olabilir. İşte bir örnek:

// Assumes you are currently working with a SocketOutputStream.

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

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

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

İçin benzer yöntemler vardır short, int, floatvb Sonra kullanabilirsiniz DataInputStream alıcı tarafında.



0

İşte dönüştürmek için başka bir yol byte[]için longJava 8 veya daha yeni kullanılarak:

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 dönüştürülmesi, longbit-OR'ye tabi iki tamsayı değerinin yüksek ve düşük dereceli bitleri olarak ifade edilebilir. Not toUnsignedLongdan Integersınıfı ve birinci çağrı toUnsignedLonggereksiz olabilir.

Diğerlerinin de belirttiği gibi ters dönüşüm de açılabilir.


0

Uzun ve ByteArray tipleri için Kotlin uzantıları:

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

Tam kodu kütüphanemde görebilirsiniz https://github.com/ArtemBotnev/low-level-extensions

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.