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



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

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

JDK URLDecoder 没有有效地实现。最值得注意的是,它在内部依赖于StringBuffer(在URLDecoder的情况下,它不必要地引入了同步(。Apache共享资源提供了URLCodec,但据报道它在性能方面也存在类似的问题,但我还没有验证在最新版本中仍然如此。

Mark A. Ziesemer不久前写了一篇关于URLDecoder的问题和性能的文章。他记录了一些错误报告,最终编写了一个完整的替代品。 因为这是这样,我将在这里引用一些关键摘录,但你真的应该在这里阅读整个源文章:http://blogger.ziesemer.com/2009/05/improving-url-coder-performance-java.html

精选语录:

Java 在 java.net.URLEncoder 和 java.net.URLDecoder.不幸的是,它不是 性能最佳,这既归功于 API 的编写方式,也 实现中的详细信息。一些与性能相关的 sun.com 上已经提交了与URLEncoder相关的错误。

还有一个替代方案:org.apache.commons.codec.net.URLCodec Apache Commons Codec.(共享资源编解码器也提供了一个有用的 Base64 编码的实现。不幸的是,维基共享资源的网址 遇到一些与Java的URLEncoder/URLDecoder相同的问题。

对JDK和共享资源的建议:

当构造任何"缓冲区"类时,例如 ByteArrayOutputStream, CharArrayWriter, StringBuilder, or StringBuffer,估计并传入估计的容量。JDK的 URLEncoder 目前为其 StringBuffer 执行此操作,但应该这样做 这也适用于其CharArrayWriter实例。Common's URLCodec 应该为其 ByteArrayOutputStream 实例执行此操作。如果类' 默认缓冲区大小太小,可能必须通过复制来调整大小 进入新的、更大的缓冲区 - 这并不完全是一个"便宜"的操作。如果 类的默认缓冲区大小太大,内存可能 不必要地浪费。

两种实现都依赖于字符集,但只接受它们 作为其字符串名称。字符集为以下位置提供了简单而小的缓存 名称查找 - 仅存储最后使用的 2 个字符集。这不应该 被依赖,并且两者都应该接受其他字符集实例 互操作性原因也是如此。

这两种实现都只处理固定大小的输入和输出。这 JDK 的 URLEncoder 仅适用于 String 实例。共享资源网址 也基于 Strings,但也适用于 byte[] 数组。这是一个 本质上阻碍高效处理的设计级约束 较大或可变长度的输入。相反,"流支持" 接口,如CharSequence,Appendable和java.nio的Buffer。 应该支持 ByteBuffer 和 CharBuffer 的实现。

请注意,com.ziesemer.utils.urlCodec 的速度是 JDK 的 3 倍以上。 URLEncoder,速度是JDK URLDecoder的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;
}

如果这没有帮助,那么我仍然不确定你在追求什么,你最好创建一个新的、更简洁的问题。 如果这是您要问的,那么请小心,因为出于多种原因,这个断章取义的例子不是一个好主意。

线程安全实际上从来都不是静态函数的真正必要(或者它是设计失败(。特别是不,如果甚至不访问类中的静态变量。

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

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

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

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

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

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

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

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

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

最新更新