วิธีที่ง่ายต่อการต่อข้อมูลอาร์เรย์สองไบต์


249

วิธีง่าย ๆ ในการเชื่อมสองbyteอาร์เรย์คืออะไร?

พูด,

byte a[];
byte b[];

ฉันจะต่อกันสองbyteอาร์เรย์และเก็บในbyteอาร์เรย์อื่นได้อย่างไร


3
หมายเหตุโปรดที่ Apache คอมมอนส์ฝรั่งของ Google System.arrayCopy, ByteBufferและ - ไม่ได้มีประสิทธิภาพ แต่อ่าน - ByteArrayOutputStreamทุกคนได้รับการคุ้มครอง เรามีคำตอบมากกว่า 7 ข้อที่ให้ไว้ที่นี่ โปรดอย่าโพสต์การหลอกอีกต่อไป
Maarten Bodewes

คำตอบ:



377

ByteArrayOutputStreamวิธีที่สง่างามที่สุดที่จะทำนี้ด้วย

byte a[];
byte b[];

ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
outputStream.write( a );
outputStream.write( b );

byte c[] = outputStream.toByteArray( );

61
@vipw เหตุผลที่ทำให้สิ่งนี้สวยงามเพราะถ้า / เมื่อคุณต้องการต่อแถวลำดับที่สามในภายหลังคุณเพียงแค่เพิ่มบรรทัดoutputStream.write( c );- คุณไม่ต้องย้อนกลับและแก้ไขบรรทัดที่คุณสร้างอาร์เรย์ไบต์ผลลัพธ์ นอกจากนี้การสั่งซื้ออาร์เรย์ใหม่ก็ง่ายเหมือนการใช้วิธี arraycopy
Wayne Uroda

2
นอกจากนี้ยังง่ายกว่ามากเมื่อทำงานกับอาร์เรย์ 2 ไบต์
gardarh

3
ไม่ว่าจะเป็นการสูญเสียซีพียูและหน่วยความจำหรือไม่นั้นขึ้นอยู่กับว่าคุณใช้งานบ่อยแค่ไหน หากเป็นพันล้านครั้งต่อวินาที - แน่นอนเพิ่มประสิทธิภาพ มิฉะนั้นความสามารถในการอ่านและการบำรุงรักษาอาจเป็นข้อพิจารณาที่ชนะ
vikingsteve

5
หากความกังวลเกี่ยวกับการใช้หน่วยความจำและ / หรือประสิทธิภาพต้องแน่ใจว่าใช้a.length + b.lengthเป็นอาร์กิวเมนต์สำหรับตัวByteArrayOutputStreamสร้าง โปรดทราบว่าวิธีนี้จะยังคงคัดลอกไบต์ทั้งหมดไปยังอาร์เรย์ใหม่เพื่อกำหนดให้c[]! พิจารณาByteBufferวิธีการแข่งขันอย่างใกล้ชิดที่ไม่เสียหน่วยความจำ
Maarten Bodewes

ฉันไม่สามารถยกนิ้วให้นี้ได้เพราะนี่เป็นเพียงข้อมูลโค้ด ไม่มีคำอธิบายของชิ้นส่วนพื้นฐานที่นี่ซึ่งเป็นส่วนที่ฉันสนใจ (และฉันคิดว่าคนส่วนใหญ่จะ) ฉันยินดีที่จะยกนิ้วให้หากมีการเปรียบเทียบประสิทธิภาพระหว่าง System # arrayCopy (Object, int, Object, int, int) และ ByteArrayOutputStream # put (byte []) และรายละเอียดว่าสถานการณ์ใดดีที่สุดสำหรับตัวเลือกทั้งสอง นอกจากนี้ที่กล่าวว่าคำตอบควรรวม arrayCopy เนื่องจากเป็นโซลูชันอื่น
searchengine27

66

นี่คือทางออกที่ดีโดยใช้ฝรั่ง 's com.google.common.primitives.Bytes:

byte[] c = Bytes.concat(a, b);

สิ่งที่ดีเกี่ยวกับวิธีนี้คือมันมีลายเซ็น varargs:

public static byte[] concat(byte[]... arrays)

ซึ่งหมายความว่าคุณสามารถเชื่อมต่อจำนวนอาร์เรย์โดยพลการในการเรียกใช้เมธอดเดียว


30

java.nio.ByteBufferเป็นไปได้ก็คือการใช้

สิ่งที่ต้องการ

ByteBuffer bb = ByteBuffer.allocate(a.length + b.length + c.length);
bb.put(a);
bb.put(b);
bb.put(c);
byte[] result = bb.array();

// or using method chaining:

byte[] result = ByteBuffer
        .allocate(a.length + b.length + c.length)
        .put(a).put(b).put(c)
        .array();

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


3
@click_whir ขออภัยที่มีคนอ่าน ReadTheDocs ByteBuffer.allocate(int)เป็นวิธีการที่คงที่ส่งกลับ instantiated java.nio.HeapByteBuffer, subclass ByteBufferของ .put()และ.compact()วิธีการ - และอื่น ๆ ที่เป็นนามธรรม-Ness - ได้รับการดูแล
kalefranz

@kalefranz ลบcompact()บรรทัดเนื่องจากไม่ถูกต้อง
Maarten Bodewes

1
ใช้ความระมัดระวังในการใช้วิธีการ ByteBuffer อาร์เรย์ () - ถ้าคุณรู้ว่าสิ่งที่คุณกำลังทำและการบำรุงรักษาไม่ได้เป็นปัญหาไม่มีการรับประกันว่าตำแหน่ง zeroeth ใน bytebuffer สอดคล้องกับดัชนี 0 ของอาร์เรย์ไบต์เสมอ ดูที่นี่ ฉันแก้ปัญหานี้โดยการออกbb.flip(); bb.get(result);แทนbyte[] result = bb.array();สาย
DarqueSandu

1
@DarqueSandu ถึงแม้ว่าคำแนะนำที่ดีโดยทั่วไปการอ่านอย่างระมัดระวังของallocateวิธีการเปิดเผยต่อไปนี้: "ตำแหน่งบัฟเฟอร์ใหม่จะเป็นศูนย์ขีด จำกัด ของมันจะเป็นความจุเครื่องหมายของมันจะไม่ได้กำหนดและองค์ประกอบแต่ละอย่างจะเริ่มต้นเป็นศูนย์ มันจะมีอาเรย์สำรองและอาเรย์ออฟของมันจะเป็นศูนย์ " ดังนั้นสำหรับรหัสเฉพาะส่วนนี้ซึ่งการByteBufferจัดสรรภายในนั้นไม่ใช่ปัญหา
Maarten Bodewes

13

อีกวิธีหนึ่งคือการใช้ฟังก์ชั่นยูทิลิตี้ (คุณสามารถทำให้เป็นวิธีการคงที่ของคลาสยูทิลิตี้ทั่วไปถ้าคุณต้องการ):

byte[] concat(byte[]...arrays)
{
    // Determine the length of the result array
    int totalLength = 0;
    for (int i = 0; i < arrays.length; i++)
    {
        totalLength += arrays[i].length;
    }

    // create the result array
    byte[] result = new byte[totalLength];

    // copy the source arrays into the result array
    int currentIndex = 0;
    for (int i = 0; i < arrays.length; i++)
    {
        System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
        currentIndex += arrays[i].length;
    }

    return result;
}

วิงวอนอย่างนั้น

byte[] a;
byte[] b;
byte[] result = concat(a, b);

มันจะทำงานสำหรับการเชื่อมต่อ 3, 4, 5 อาร์เรย์ ฯลฯ

การทำเช่นนี้จะช่วยให้คุณได้รับประโยชน์จากรหัสอาเรย์โค้ดอย่างรวดเร็วซึ่งง่ายต่อการอ่านและบำรุงรักษา



11

หากคุณต้องการByteBufferเช่น @kalefranz มีความเป็นไปได้ที่จะเชื่อมสองbyte[](หรือมากกว่า) เข้าด้วยกันในบรรทัดเดียวเช่นนี้:

byte[] c = ByteBuffer.allocate(a.length+b.length).put(a).put(b).array();

เช่นเดียวกับคำตอบนี้แต่เกิน 1 ปี ใช้วิธีการผูกมัด แต่จะดีกว่าที่จะใส่ลงไปในคำตอบที่มีอยู่
Maarten Bodewes

11

คุณสามารถใช้ห้องสมุดบุคคลที่สามสำหรับรหัสสะอาดเช่น Apache Commons Lang และใช้เช่น:

byte[] bytes = ArrayUtils.addAll(a, b);

1
ฉันพยายามArrayUtils.addAll(a, b)และbyte[] c = Bytes.concat(a, b)แต่หลังเป็นได้เร็วขึ้น
Carlos AndrésGarcía

อาจจะ. ฉันไม่รู้จักห้องสมุด Guava ดังนั้นถ้าเป็นเช่นนั้นจะเป็นการดีกว่าที่จะใช้ คุณตรวจสอบอาร์เรย์ที่มีขนาดใหญ่มากหรือไม่
Tomasz Przybylski

1
เมื่อฉันทำการทดสอบชุด Firts คือความยาว 68 องค์ประกอบ y ความยาว 8790688 ที่สอง
Carlos AndrésGarcía

5

สำหรับสองหรือหลายอาร์เรย์วิธียูทิลิตี้ที่เรียบง่ายและสะอาดนี้สามารถใช้ได้:

/**
 * Append the given byte arrays to one big array
 *
 * @param arrays The arrays to append
 * @return The complete array containing the appended data
 */
public static final byte[] append(final byte[]... arrays) {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    if (arrays != null) {
        for (final byte[] array : arrays) {
            if (array != null) {
                out.write(array, 0, array.length);
            }
        }
    }
    return out.toByteArray();
}

1
สิ่งนี้ทำให้สูญเสียความทรงจำ วิธีการนี้ใช้ได้กับสองอาร์เรย์ที่เล็กกว่า แต่แน่นอนว่าจะเก็บภาษีตัวเก็บขยะสำหรับอาร์เรย์เพิ่มเติม
Maarten Bodewes

1

ผสานสองไบต์ PDF อาร์เรย์

หากคุณกำลังรวมอาร์เรย์สองไบต์ซึ่งมี PDF ตรรกะนี้จะไม่ทำงาน เราจำเป็นต้องใช้เครื่องมือของบุคคลที่สามเช่น PDFbox จาก Apache:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
mergePdf.addSource(new ByteArrayInputStream(a));
mergePdf.addSource(new ByteArrayInputStream(b));
mergePdf.setDestinationStream(byteArrayOutputStream);
mergePdf.mergeDocuments();
c = byteArrayOutputStream.toByteArray();

ปิดหัวข้อสำหรับคำถามนี้ แต่เป็นสิ่งที่ฉันต้องการ
amos

1

หากคุณไม่ต้องการยุ่งกับขนาดของอาร์เรย์เพียงใช้เวทย์มนตร์ของการต่อสตริง:

byte[] c = (new String(a, "l1") + new String(b, "l1")).getBytes("l1");

หรือกำหนดบางแห่งในรหัสของคุณ

// concatenation charset
static final java.nio.charset.Charset cch = java.nio.charset.StandardCharsets.ISO_8859_1;

และการใช้งาน

byte[] c = (new String(a, cch) + new String(b, cch)).getBytes(cch);

แน่นอนว่ายังใช้งานได้กับการต่อสตริงมากกว่าสองรายการโดยใช้ตัว+ดำเนินการเพิ่ม


ทั้งสอง"l1"และISO_8859_1ระบุชุดอักขระ Western Latin 1 ที่เข้ารหัสอักขระแต่ละตัวเป็นไบต์เดียว เนื่องจากไม่มีการแปลแบบหลายไบต์อักขระในสตริงจะมีค่าเหมือนกับไบต์ (ยกเว้นว่าพวกเขาจะถูกตีความว่าเป็นค่าบวกเสมอเช่นcharไม่ได้ลงนาม) อย่างน้อยสำหรับรันไทม์ที่ Oracle จัดเตรียมไว้ไบต์ใด ๆ จะถูก "ถอดรหัส" อย่างถูกต้องแล้วจึง "เข้ารหัส" อีกครั้ง

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


@MaartenBodewes หากคุณไม่แน่ใจเกี่ยวกับ "l1" (ซึ่งเป็นเพียงนามแฝงสำหรับ ISO 8859-1) อย่าใช้คำว่า "แน่นอน" ค่าไบต์ใดที่จะถูกลบทิ้ง? สำหรับการใช้งานหน่วยความจำคำถามนั้นเกี่ยวกับวิธีที่ง่ายในการเชื่อมต่อสองอาร์เรย์ไบต์ไม่ใช่เรื่องประสิทธิภาพของหน่วยความจำส่วนใหญ่
John McClane

1
ฉันได้วางคำเตือนแล้วทำการทดสอบ สำหรับละติน 1 และ Oracle ที่ให้บริการรันไทม์ (11) สิ่งนี้ดูเหมือนจะใช้ได้ ดังนั้นฉันจึงให้ข้อมูลเพิ่มเติมและลบความคิดเห็นและ downvote ของฉัน ฉันหวังว่าไม่เป็นไรสำหรับคุณมิฉะนั้นโปรดย้อนกลับ
Maarten Bodewes

0

นี่คือวิธีของฉันที่จะทำ!

public static byte[] concatByteArrays(byte[]... inputs) {
    int i = inputs.length - 1, len = 0;
    for (; i >= 0; i--) {
        len += inputs[i].length;
    }
    byte[] r = new byte[len];
    for (i = inputs.length - 1; i >= 0; i--) {
        System.arraycopy(inputs[i], 0, r, len -= inputs[i].length, inputs[i].length);
    }
    return r;
}

คุณสมบัติ :

  • ใช้ varargs (... ) เพื่อเรียกด้วยจำนวนไบต์ใด ๆ []
  • การใช้งานSystem.arraycopy()ที่ได้รับการติดตั้งมาพร้อมกับรหัสเนทีฟเฉพาะเครื่องเพื่อให้มั่นใจถึงการทำงานด้วยความเร็วสูง
  • สร้างไบต์ใหม่ [] ด้วยขนาดที่แน่นอนที่ต้องการ
  • จัดสรรintตัวแปรให้น้อยลงโดยการใช้ซ้ำiและlenตัวแปร
  • การเปรียบเทียบที่เร็วขึ้นกับค่าคงที่

โปรดทราบ :

วิธีที่ดีกว่าที่จะทำเช่นนี้โดยการคัดลอกรหัส @ Jonathan ปัญหามาจากอาร์เรย์ตัวแปรเนทีฟเนื่องจาก Java สร้างตัวแปรใหม่เมื่อชนิดข้อมูลนี้ถูกส่งผ่านไปยังฟังก์ชันอื่น


1
ไม่นั่นเป็นวิธีที่เวย์นจะทำคุณมา 5 ปีแล้ว
Maarten Bodewes

@MaartenBodewes ขอบคุณคุณฉันใช้ความคิดเห็นของคุณในการเขียนโปรแกรมวันนี้ตอนนี้แตกต่างและมีประสิทธิภาพที่ดีขึ้น
Daniel De León

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