2016-10-06 20 views
1

バイナリ配列のリストとしていくつかのバイナリデータを返すライブラリがあります。これらのbyte []はInputStreamにマージする必要があります。Java 8 - 最も効率的な方法をマージするリスト<byte[]>をバイト[]

これは私の現在の実装である:

public static InputStream foo(List<byte[]> binary) { 
    byte[] streamArray = null; 
    binary.forEach(bin -> { 
     org.apache.commons.lang.ArrayUtils.addAll(streamArray, bin); 
    }); 
    return new ByteArrayInputStream(streamArray); 
} 

が、これはかなりのCPU激しいです。より良い方法がありますか?

すべての回答ありがとうございます。私はパフォーマンステストをしました。

  • 機能:100の通話
  • 機能の 'NicolasFilottoEstSize' => 65,24ミリ秒の平均:100の通話
  • 機能の 'NicolasFilotto' => 68,04ミリ秒の平均それらは私の結果です " NicolasFilottoSequenceInputStream」100の通話
  • 機能上=> 63,09ミリ秒の平均: 'Saka1029_1' 100の通話
  • 上=> 63,06ミリ秒の平均機能: '100の通話
  • 上Saka1029_2' => 0.79ミリ秒の平均
  • 機能: 'Coco' => 10通話で平均541,60ms

私は、これは実行機能です...

'Saka1029_2' が正しく測定されたかはわからない:私はすべての入力ストリーム

それらを読んで

private static double execute(Callable<InputStream> funct, int times) throws Exception { 
    List<Long> executions = new ArrayList<>(times); 

    for (int idx = 0; idx < times; idx++) { 
     BufferedReader br = null; 
     long startTime = System.currentTimeMillis(); 
     InputStream is = funct.call(); 
     br = new BufferedReader(new InputStreamReader(is)); 
     String line = null; 
     while ((line = br.readLine()) != null) {} 
     executions.add(System.currentTimeMillis() - startTime); 
    } 

    return calculateAverage(executions); 
} 

ノート使用される実装は次のとおりです。

public static class NicolasFilotto implements Callable<InputStream> { 

    private final List<byte[]> binary; 

    public NicolasFilotto(List<byte[]> binary) { 
     this.binary = binary; 
    } 

    @Override 
    public InputStream call() throws Exception { 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     for (byte[] bytes : binary) { 
      baos.write(bytes, 0, bytes.length); 
     } 
     return new ByteArrayInputStream(baos.toByteArray()); 
    } 

} 

public static class NicolasFilottoSequenceInputStream implements Callable<InputStream> { 

    private final List<byte[]> binary; 

    public NicolasFilottoSequenceInputStream(List<byte[]> binary) { 
     this.binary = binary; 
    } 

    @Override 
    public InputStream call() throws Exception { 
     return new SequenceInputStream(
       Collections.enumeration(
         binary.stream().map(ByteArrayInputStream::new).collect(Collectors.toList()))); 
    } 

} 

public static class NicolasFilottoEstSize implements Callable<InputStream> { 

    private final List<byte[]> binary; 
    private final int lineSize; 

    public NicolasFilottoEstSize(List<byte[]> binary, int lineSize) { 
     this.binary = binary; 
     this.lineSize = lineSize; 
    } 

    @Override 
    public InputStream call() throws Exception { 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(binary.size() * lineSize); 
     for (byte[] bytes : binary) { 
      baos.write(bytes, 0, bytes.length); 
     } 
     return new ByteArrayInputStream(baos.toByteArray()); 
    } 

} 

public static class Saka1029_1 implements Callable<InputStream> { 

    private final List<byte[]> binary; 

    public Saka1029_1(List<byte[]> binary) { 
     this.binary = binary; 
    } 

    @Override 
    public InputStream call() throws Exception { 
     byte[] all = new byte[binary.stream().mapToInt(a -> a.length).sum()]; 
     int pos = 0; 
     for (byte[] bin : binary) { 
      int length = bin.length; 
      System.arraycopy(bin, 0, all, pos, length); 
      pos += length; 
     } 
     return new ByteArrayInputStream(all); 
    } 

} 

public static class Saka1029_2 implements Callable<InputStream> { 

    private final List<byte[]> binary; 

    public Saka1029_2(List<byte[]> binary) { 
     this.binary = binary; 
    } 

    @Override 
    public InputStream call() throws Exception { 
     int size = binary.size(); 
     return new InputStream() { 
      int i = 0, j = 0; 

      @Override 
      public int read() throws IOException { 
       if (i >= size) return -1; 
       if (j >= binary.get(i).length) { 
        ++i; 
        j = 0; 
       } 
       if (i >= size) return -1; 
       return binary.get(i)[j++]; 
      } 
     }; 
    } 

} 

public static class Coco implements Callable<InputStream> { 

    private final List<byte[]> binary; 

    public Coco(List<byte[]> binary) { 
     this.binary = binary; 
    } 

    @Override 
    public InputStream call() throws Exception { 
     byte[] streamArray = new byte[0]; 
     for (byte[] bin : binary) { 
      streamArray = org.apache.commons.lang.ArrayUtils.addAll(streamArray, bin); 
     } 
     return new ByteArrayInputStream(streamArray); 
    } 

} 
+0

あなたはとのInputStreamを使用することができます最初の[]はすべてのバイトを生成することなく一覧から読み込みます。 –

+1

'SequenceInputStream'に基づく私の2番目の提案はどうですか? –

+0

マイクロベンチマークのために、JMHを使用してください。http://openjdk.java.net/projects/code-tools/jmh/ –

答えて

2

はこれを試してみてください。

public static InputStream foo(List<byte[]> binary) { 
    byte[] all = new byte[binary.stream().mapToInt(a -> a.length).sum()]; 
    int pos = 0; 
    for (byte[] bin : binary) { 
     int length = bin.length; 
     System.arraycopy(bin, 0, all, pos, length); 
     pos += length; 
    } 
    return new ByteArrayInputStream(all); 
} 

それとも

public static InputStream foo(List<byte[]> binary) { 
    int size = binary.size(); 
    return new InputStream() { 
     int i = 0, j = 0; 
     @Override 
     public int read() throws IOException { 
      if (i >= size) return -1; 
      if (j >= binary.get(i).length) { 
       ++i; 
       j = 0; 
      } 
      if (i >= size) return -1; 
      return binary.get(i)[j++]; 
     } 
    }; 
} 
+0

@NicolasFilottoしかしこれはバイト配列を1回だけ割り当てます。 – saka1029

3

ByteArrayOutputStreamを使用してリストの各バイト配列のコンテンツを格納することはできますが、効率を上げるためには、 のインスタンスを作成し、ターゲットサイズとできるだけ一致する初期サイズにする必要があります。サイズやバイトの配列の少なくとも平均サイズを知って、あなたはそれを使用する必要があり、コードは次のようになります。

public static InputStream foo(List<byte[]> binary) { 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(ARRAY_SIZE * binary.size()); 
    for (byte[] bytes : binary) { 
     baos.write(bytes, 0, bytes.length); 
    } 
    return new ByteArrayInputStream(baos.toByteArray()); 
} 

別のアプローチは、論理的にすべてのByteArrayInputStreamを連結するためにSequenceInputStreamを使用することですあなたのリストの1つの要素を表すインスタンスは、次のようになります。

このアプローチの興味深い点は、何もコピーする必要がないということです。バイト配列をそのまま使用するByteArrayInputStreamのインスタンスのみを作成します。@Holgerによって提案されたように、あなたの最初のListが大きい場合は、あなたが直接、我々は簡単に行うことができますenumerationiteratorを変換する必要があります、iterator()を呼び出すことができ、特にコストがListとして結果を収集避けるため

Apache Commons CollectionからIteratorUtils.asEnumeration(iterator)で、最終的なコードは次のようになります。

public static InputStream foo(List<byte[]> binary) { 
    return new SequenceInputStream(
     IteratorUtils.asEnumeration(
      binary.stream().map(ByteArrayInputStream::new).iterator() 
     ) 
    ); 
} 
+0

java8 parralelを利用する方法はありますか? – Coco

+1

@Coco: 'SequenceInputStream'アプローチでは、実行する実際の作業がないため、並列処理の必要はありません。 – Holger

+1

@Nicolas Filotto:リスト要素ごとの作業負荷は非常に小さいので、リストの大きさに関係なく、何のメリットもありません。要点は、「Collectors.toList()」は、マージ・ステップの作業負荷が以前の同時蓄積の最大節約とまったく同じくらい大きいので、並列処理のメリットがないことです。並列処理の恩恵を受ける唯一の方法は、軽量ByteArrayInputStream :: new'の同時実行です。これは、スレッド管理のオーバーヘッドを補うものではありません。 – Holger

関連する問題