文字列の大規模なコレクションまたは配列をURLエンコードまたはデコードする必要があることがよくあります。それらを反復処理して静的URLDecoder.decode(string、 "UTF-8")を使用する以外に、このタイプの操作のパフォーマンスを向上させるライブラリはありますか?
同僚は、静的メソッドを使用して文字列をインプレースでデコードすることはスレッドセーフではないと主張しています。なぜそうなるのでしょうか?
JDKURLDecoderが効率的に実装されていませんでした。最も注目すべきは、内部的にはStringBufferに依存していることです(URLDecoderの場合は不必要に同期が導入されます)。Apache commonsはURLCodecを提供しますが、パフォーマンスに関して同様の問題があることも報告されていますが、最新バージョンでそれがまだ当てはまることを確認していません。
Mark A. Ziesemerは、URLDecoderの問題とパフォーマンスについてしばらく前に投稿しました。彼はいくつかのバグレポートを記録し、完全な代替品を書くことになりました。これはSOであるため、ここでいくつかの重要な抜粋を引用しますが、実際にはここでソース記事全体を読む必要があります:http: //blogger.ziesemer.com/2009/05/improving-url-coder-performance-java.html
選択された引用:
Javaは、java.net.URLEncoderおよびjava.net.URLDecoderでこの機能のデフォルトの実装を提供します。残念ながら、APIの記述方法と実装内の詳細の両方が原因で、最高のパフォーマンスではありません。URLEncoderに関連して、パフォーマンスに関連する多数のバグがsun.comに報告されています。
別の方法があります:ApacheCommonsCodecのorg.apache.commons.codec.net.URLCodec。(Commons Codecは、Base64エンコーディングの便利な実装も提供します。)残念ながら、CommonsのURLCodecには、JavaのURLEncoder/URLDecoderと同じ問題がいくつかあります。
..。
JDKとCommonsの両方に関する推奨事項:
ByteArrayOutputStream、CharArrayWriter、StringBuilder、StringBufferなどの「バッファ」クラスのいずれかを構築する場合は、推定容量を見積もり、渡します。JDKのURLEncoderは現在、StringBufferに対してこれを実行しますが、CharArrayWriterインスタンスに対してもこれを実行する必要があります。CommonのURLCodecは、ByteArrayOutputStreamインスタンスに対してこれを行う必要があります。クラスのデフォルトのバッファサイズが小さすぎる場合は、新しい大きなバッファにコピーしてサイズを変更する必要がある場合があります。これは、正確には「安価な」操作ではありません。クラスのデフォルトのバッファサイズが大きすぎると、メモリが不必要に浪費される可能性があります。
どちらの実装もCharsetsに依存していますが、文字列名としてのみ受け入れます。文字セットは、名前検索用のシンプルで小さなキャッシュを提供します-使用された最後の2つの文字セットのみを保存します。これは信頼されるべきではなく、他の相互運用性の理由からも両方ともCharsetインスタンスを受け入れる必要があります。
どちらの実装も、固定サイズの入力と出力のみを処理します。JDKのURLEncoderは、Stringインスタンスでのみ機能します。CommonsのURLCodecもStringsに基づいていますが、byte[]配列でも機能します。これは設計レベルの制約であり、より大きな入力または可変長の入力の効率的な処理を本質的に妨げます。代わりに、CharSequence、Appendable、java.nioのByteBufferおよびCharBufferのBuffer実装などの「ストリームをサポートする」インターフェースをサポートする必要があります。
..。
com.ziesemer.utils.urlCodecは、JDK URLEncoderの3倍以上、JDKURLDecoderの1.5倍以上高速であることに注意してください。(JDKのURLDecoderはURLEncoderよりも高速だったため、改善の余地はあまりありませんでした。)
あなたの同僚は、URLDecodeがスレッドセーフではないと示唆するのは間違っていると思います。ここでの他の答えは詳細に説明します。
編集[2012-07-03]-OPによって投稿された後のコメントごと
もっとアイデアを探しているかどうかわかりませんか?リストをアトミックコレクションとして操作する場合は、メソッド外の参照を含め、リストへのすべてのアクセスを同期する必要があるのは正しいことです。ただし、返されるリストの内容が元のリストと異なる可能性がある場合は、他のスレッドによって変更される可能性のあるコレクションからの文字列の「バッチ」を操作するためのブルートフォースアプローチは次のようになります。
/**
* @param origList will be copied by this method so that origList can continue
* to be read/write by other threads.
* @return list containing decoded strings for each entry that was
in origList at time of copy.
*/
public List<String> decodeListOfStringSafely(List<String> origList)
throws UnsupportedEncodingException {
List<String> snapshotList = new ArrayList<String>(origList);
List<String> newList = new ArrayList<String>();
for (String urlStr : snapshotList) {
String decodedUrlStr = URLDecoder.decode(urlStr, "UTF8");
newList.add(decodedUrlStr);
}
return newList;
}
それでも問題が解決しない場合は、あなたが何を求めているのかまだわかりません。新しい、より簡潔な質問を作成する方がよいでしょう。それがあなたが求めていたものである場合、文脈から外れたこの例は多くの理由で良い考えではないので注意してください。
Apacheには、デコードのエンコードに使用できるURLCodecがあります。
静的メソッドがローカル変数または最終的に初期化された変数に対してのみ機能する場合は、完全にスレッドセーフです。
パラメータはスタック上に存在し、完全にスレッドセーフであるため、最終定数は不変であり、変更できません。
次のコードは完全にスレッドセーフです。
public static String encodeMyValue(String value){
// do encoding here
}
最終変数が変更可能である場合、つまり再割り当てはできないが、その内部表現(プロパティ)は変更できる場合は注意が必要です。
静的関数ではスレッドセーフが実際に必要になることはありません(または設計上の失敗です)。特に、クラス内の静的変数にさえアクセスしない場合はそうではありません。
以前に使用した関数を使用して、コレクションを反復処理することをお勧めします
基本的に、静的メソッド、インスタンスメソッド、またはコンストラクターに適用される魔法のスレッドセーフはありません。同期が適用されない限り、これらはすべて複数のスレッドで同時に呼び出すことができます。共有データをフェッチまたは変更しない場合、通常は安全です。共有データにアクセスする場合は、さらに注意する必要があります。
したがって、あなたの場合、このurldecodingまたはencodingに同期メソッドを記述して、外部でスレッドセーフを適用できます。