我经常要对大型集合或字符串数组进行url编码或解码。除了迭代它们并使用静态URLDecoder.decode(字符串,“UTF-8”)之外,是否有任何库可以使这种类型的操作更高效?
一位同事坚持认为使用静态方法就地解码字符串并不是线程安全的。为什么会这样?
答案 0 :(得分: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。不幸的是,事实并非如此 由于API的编写方式以及表达方式,表现最佳 实施细节。一些与性能有关的 已经在sun.com上提交了与URLEncoder相关的错误。
还有一个替代方案:org.apache.commons.codec.net.URLCodec来自 Apache Commons Codec。 (Commons Codec也提供了一个有用的 Base64编码的实现。)不幸的是,Commons的URLCodec 遇到了一些与Java的URLEncoder / URLDecoder相同的问题。
...
JDK和Commons的建议:
构造任何“缓冲”类时,例如 ByteArrayOutputStream,CharArrayWriter,StringBuilder或 StringBuffer,估计并传入估计的容量。 JDK的 URLEncoder目前为其StringBuffer执行此操作,但应该这样做 这也是它的CharArrayWriter实例。 Common的URLCodec 应该为其ByteArrayOutputStream实例执行此操作。如果班级' 默认缓冲区大小太小,可能需要通过复制来调整大小 进入新的,更大的缓冲区 - 这不是一个“廉价”的操作。如果 类的默认缓冲区大小太大,内存可能是 不必要地浪费了。
两种实现都依赖于Charsets,但只接受它们 作为他们的String名称。 Charset提供简单的小缓存 名称查找 - 仅存储最后使用的2个字符集。这不应该 可以依赖,两者都应该接受其他的Charset实例 互操作性的原因。
两种实现仅处理固定大小的输入和输出。该 JDK的URLEncoder仅适用于String实例。 Commons'URLCodec 也是基于字符串,但也适用于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;
}
如果这没有帮助,那么我仍然不确定你追求的是什么,你会更好地创造一个新的,更简洁的问题。如果这就是你所要求的,那么要小心,因为这个例子脱离上下文并不是一个好主意,原因很多。
答案 1 :(得分:0)
Apache有URLCodec可用于编码解码。
如果静态方法只适用于局部变量或最终初始化变量,则它完全是线程安全的。
由于参数存在于堆栈上并且它们完全是线程安全的,因此最终常量是不可变的,因此无法更改。
以下代码完全是线程安全的:
public static String encodeMyValue(String value){
// do encoding here
}
如果最终变量是可变的,则应该小心,这意味着您无法重新分配它,但您可以更改其内部表示(属性)。
答案 2 :(得分:0)
线程安全实际上从来没有真正需要静态功能(或者它是设计失败)。特别是,如果不在课堂上访问静态变量。
我建议使用您之前使用的功能,并遍历集合
答案 3 :(得分:0)
基本上没有应用于静态方法或实例方法或构造函数的魔术线程安全性。除非同步应用,否则它们可以同时在多个线程上调用。如果他们不提取或更改任何共享数据,他们通常是安全的 - 如果他们确实访问共享数据,您需要更加小心。
所以在你的情况下,你可以在这个urldecoding或编码之上编写同步方法,通过它可以在外部强制执行线程安全。