是否有一个通用的Java库来处理字符串集合的URL编码/解码?

我经常需要对大型集合或字符串数​​组进行url编码或解码。 除了遍历它们并使用静态URLDecoder.decode(字符串,“UTF-8”)之外,是否有任何库可以使这种类型的操作更高效?

一位同事坚持使用静态方法就地解码字符串不是线程安全的。 那为什么会这样?

JDK URLDecoder没有有效实现。 最值得注意的是,内部它依赖于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中提供了此function的默认实现。 不幸的是,由于API的编写方式以及实现中的细节,它并不是最佳性能。 在sun.com上提交了许多与URLEncoder相关的性能相关错误。

还有一个替代方案:来自Apache Commons Codec的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实例执行此操作。 如果类的默认缓冲区大小太小,它们可能必须通过复制到新的更大的缓冲区来resize – 这不是一个“便宜”的操作。 如果类的默认缓冲区大小太大,则可能会不必要地浪费内存。

这两种实现都依赖于Charsets,但只接受它们作为String名称。 Charset为名称查找提供了一个简单的小缓存 – 仅存储最后使用的2个字符集。 这不应该依赖,并且由于其他互操作性原因,它们都应该接受Charset实例。

两种实现仅处理固定大小的输入和输出。 JDK的URLEncoder仅适用于String实例。 Commons的URLCodec也基于字符串,但也适用于byte []数组。 这是一种设计级约束,实质上阻止了对较大或可变长度输入的有效处理。 相反,应支持“流支持”接口,如CharSequence,Appendable和java.nio的ByteBuffer和CharBuffer的Buffer实现。

请注意,com.ziesemer.utils.urlCodec的速度是JDK URLEncoder的3倍,速度是JDK URLDecoder的1.5倍。 (JDK的URLDecoder比URLEncoder更快,因此没有太多改进空间。)

我认为你的同事建议URLDecode不是线程安全的。 其他答案在这里详细解释。

编辑[2012-07-03] – 每个评论由OP发布

不确定您是否在寻找更多创意? 你是正确的,如果你打算在列表上作为primefaces集合进行操作,那么你必须同步对列表的所有访问,包括方法之外的引用。 但是,如果您对返回的列表内容可能与原始列表有所不同,那么对于可能由其他线程修改的集合中的“批量”字符串进行操作的powershell方法可能如下所示:

/** * @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 decodeListOfStringSafely(List origList) throws UnsupportedEncodingException { List snapshotList = new ArrayList(origList); List newList = new ArrayList(); for (String urlStr : snapshotList) { String decodedUrlStr = URLDecoder.decode(urlStr, "UTF8"); newList.add(decodedUrlStr); } return newList; } 

如果这没有帮助,那么我仍然不确定你在追求什么,你会更好地创造一个新的,更简洁的问题。 如果这就是你所要求的,那么要小心,因为这个例子脱离上下文并不是一个好主意,原因有很多。

实际上,线程安全对于静态函数来说并不是必需的(或者它是设计失败)。 尤其不是,如果甚至不在类中访问静态变量。

我建议使用之前使用的函数,并遍历集合

基本上没有应用于静态方法或实例方法或构造函数的神奇线程安全性。 除非同步应用,否则可以同时在多个线程上调用它们。 如果他们不提取或更改任何共享数据,他们通常是安全的 – 如果他们确实访问共享数据,您需要更加小心。

所以在您的情况下,您可以在此urldecoding或编码之上编写同步方法,您可以通过该方法在外部强制执行线程安全。

Apache有URLCodec ,可用于编码解码。

如果静态方法只适用于局部变量或最终初始化变量,那么它完全是线程安全的。

由于参数存在于堆栈上并且它们完全是线程安全的,因此最终常量是不可变的,因此无法更改。

以下代码完全是线程安全的:

 public static String encodeMyValue(String value){ // do encoding here } 

如果最终变量是可变的,则应该小心,这意味着您无法重新分配它,但您可以更改其内部表示(属性)。