5

Deflater.setLevel()が期待どおりに機能しません。

static void test1() throws Exception {
     byte[] output = new byte[20];
     Deflater compresser = new Deflater();
     // compresser.setLevel(Deflater.BEST_COMPRESSION);
     compresser.setInput("blah".getBytes("UTF-8"));
     compresser.finish();
     int len = compresser.deflate(output);
     System.out.println("len="+ len+ " " +Arrays.toString(output));
}

上記は私にとっては問題なく機能します(Java 7)が、compresser.setLevel()行のコメントを外すと壊れます(deflate()0バイトを返します)。を除いて、どの圧縮レベルでも同じことが起こりますDEFAULT。より具体的には、レベルセットがコンストラクターで設定されたもの (明示的または暗黙的に、ここのように) と同じである場合にのみ「機能」します (むしろ、無害です)。つまり、それは役に立たない場合にのみ使用できます。

Ideoneで例を参照してください。

この質問は同じ問題を指しており、受け入れられた答えは基本的に次のように述べています。セッターでレベルを設定しないで、コンストラクターで設定してください。満足にはほど遠い、IMO - なぜsetLevel()存在するのですか? 壊れていますか、それとも何か不足していますか?

4

2 に答える 2

5

JDKのソースコードを少し掘り下げました。それは確かにレベルを設定します。を でフォローするsetLevel()compresser.deflate(new byte[0]);、動作します。

起こっていることは、deflate()後の最初の呼び出しでsetLevel()レベルが変更されたことを確認し、zlib のdeflateParams()関数を呼び出して変更することです。 deflateParams()次に、利用可能なデータを圧縮しますが、要求したという事実finish()は渡されません。deflate()その場合、JDK 実装はを呼び出しませんZ_FINISH。その結果、指定したデータは圧縮のために送信され、コンプレッサーはデータを蓄積しますが、終了するように求められていないため、圧縮されたブロックを発行しません。だからあなたは何も得ません。

実際にレベルを設定するには、deflate()の後にを呼び出す必要があります。setLevel()その後、後続のデータは新しいレベルで圧縮されます。

後の最初のdeflate()呼び出しまでに提供されたデータはsetLevel()古い圧縮レベルで圧縮されることに注意することが重要です。deflate()その呼び出しの後に提供されたデータのみが新しいレベルを使用します。したがって、あなたの例deflate()で最後のものの後に単純に別のことをした場合、それが適用され、finish()圧縮されたデータが得られますが、デフォルトの圧縮レベルが使用されます。

于 2013-07-13T21:17:16.360 に答える
3

これはバグだと思います。

ソース コードを調べると、実際に圧縮レベルを設定するネイティブ メソッドinitを呼び出すのはコンストラクターのみであることがわかります。圧縮レベルは、ネイティブ呼び出しが行われる前、つまりオブジェクトの作成前に設定する必要があるようです。initDeflater

オブジェクトのsetLevel(int)レベルを表面的に設定するだけです。ネイティブ ライブラリへの呼び出しはありません。

于 2013-07-13T18:15:27.303 に答える