Java 正在使用StringTokenizer复制String.split
受此鼓舞,并且我有数十亿个字符串需要解析,我尝试修改代码以接受StringTokenizer而不是string[] 在我和得到美味的x2性能提升之间剩下的唯一一件事是,当你Java 正在使用StringTokenizer复制String.split,java,performance,string,split,stringtokenizer,Java,Performance,String,Split,Stringtokenizer,受此鼓舞,并且我有数十亿个字符串需要解析,我尝试修改代码以接受StringTokenizer而不是string[] 在我和得到美味的x2性能提升之间剩下的唯一一件事是,当你 "dog,,cat".split(",") //output: ["dog","","cat"] StringTokenizer("dog,,cat") // nextToken() = "dog" // nextToken() = "cat" 如何使用StringTokenizer获得类似的结果?有没有更快的方法可以做
"dog,,cat".split(",")
//output: ["dog","","cat"]
StringTokenizer("dog,,cat")
// nextToken() = "dog"
// nextToken() = "cat"
如何使用StringTokenizer获得类似的结果?有没有更快的方法可以做到这一点?您是否只在逗号上标记?如果是这样的话,我会编写自己的标记器-它可能会比更通用的StringTokenizer更高效,后者可以查找多个标记,并且您可以让它按照自己的意愿运行。对于这样一个简单的用例,它可以是一个简单的实现 如果有用的话,您甚至可以实现
Iterable
,并通过强类型而不是StringTokenizer
提供的Enumeration
支持来增强for循环支持。让我知道如果你想要任何帮助编码这样一个野兽-它真的不应该太难
此外,在脱离现有解决方案太远之前,我会尝试对实际数据运行性能测试。您知道有多少执行时间实际上花在了
String.split
?我知道你有很多字符串要解析,但是如果你以后对它们做了任何有意义的事情,我希望这比拆分更重要。根据需要标记的字符串类型,你可以基于String.indexOf()编写自己的拆分器。您还可以创建一个多核解决方案来进一步提高性能,因为字符串的标记化是相互独立的。工作批量-让我们说-100字符串每个核心。执行String.split()或其他操作。如果您的输入是结构化的,您可以查看JavaCC编译器。它生成一个java类来读取您的输入。它看起来是这样的:
TOKEN { <CAT: "cat"> , <DOG:"gog"> }
input: (cat() | dog())*
cat: <CAT>
{
animals.add(new Animal("Cat"));
}
dog: <DOG>
{
animals.add(new Animal("Dog"));
}
TOKEN{,}
输入:(cat()| dog())*
类别:
{
动物。添加(新动物(“猫”);
}
狗:
{
动物。添加(新动物(“狗”);
}
您可以尝试Apache Commons Lang中的StrTokenizer类,而不是StringTokenizer,我引用它:
此类可以将字符串拆分为许多较小的字符串。它旨在完成与StringTokenizer类似的工作,但是它提供了更多的控制和灵活性,包括实现ListIterator接口
空令牌可以被删除或作为null返回
这听起来像是您需要的,我想?注意:在做了一些快速基准测试之后,Scanner的速度比String.split慢四倍左右。因此,不要使用扫描仪。 (我留下这篇帖子是为了记录这样一个事实:在这种情况下,扫描仪是个坏主意。) 假设您使用的是Java 1.5或更高版本,请尝试实现
迭代器
,具体如下:
Scanner sc = new Scanner("dog,,cat");
sc.useDelimiter(",");
while (sc.hasNext()) {
System.out.println(sc.next());
}
给出:
dog
cat
你可以这样做。这并不完美,但可能对你有用
public static List<String> find(String test, char c) {
List<String> list = new Vector<String>();
start;
int i=0;
while (i<=test.length()) {
int start = i;
while (i<test.length() && test.charAt(i)!=c) {
i++;
}
list.add(test.substring(start, i));
i++;
}
return list;
}
公共静态列表查找(字符串测试,字符c){
列表=新向量();
开始
int i=0;
而(i嗯,你能做的最快的事情就是手动遍历字符串,例如
List<String> split(String s) {
List<String> out= new ArrayList<String>();
int idx = 0;
int next = 0;
while ( (next = s.indexOf( ',', idx )) > -1 ) {
out.add( s.substring( idx, next ) );
idx = next + 1;
}
if ( idx < s.length() ) {
out.add( s.substring( idx ) );
}
return out;
}
列表拆分(字符串s){
List out=new ArrayList();
int-idx=0;
int next=0;
而((next=s.indexOf(',',idx))>-1){
添加(s.substring(idx,next));
idx=下一个+1;
}
如果(idx
这个(非正式测试)看起来是split的两倍。但是,以这种方式进行迭代有点危险,例如,它会在转义逗号上中断,如果您最终需要在某个点上处理它(因为您的10亿字符串列表有3个转义逗号)当你考虑到这一点时,你可能最终会失去一些速度优势
最终,它可能不值得这么麻烦。在修改了这个类之后,我找不到一种方法来满足返回[“dog”,“cat”]
的要求
此外,StringTokenizer
类仅出于兼容性原因而保留,并且鼓励使用String.split
。根据StringTokenizer
的API规范:
StringTokenizer
是一个遗留类
这是为了兼容性而保留的
原因虽然它的使用是有限的
在新代码中不鼓励。它是
建议任何寻求此服务的人
功能使用split
方法
String
或java.util.regex
改为打包
由于问题在于该方法的性能较差,因此我们需要找到替代方法
注意:我说的是“假定性能差”因为很难确定每个用例都会导致StringTokenizer
优于String.split
方法。此外,在许多情况下,除非字符串的标记化确实是通过适当的分析确定的应用程序的瓶颈,否则我觉得它最终会成为一个瓶颈重新优化,如果有的话。我倾向于说,在冒险进行优化之前,编写有意义且易于理解的代码
现在,从目前的需求来看,滚动我们自己的标记器可能不会太困难
推出我们自己的tokenzier!
下面是我编写的一个简单的标记器。我应该注意,没有速度优化,也没有错误检查来防止超过字符串的结尾——这是一个快速而肮脏的实现:
class MyTokenizer implements Iterable<String>, Iterator<String> {
String delim = ",";
String s;
int curIndex = 0;
int nextIndex = 0;
boolean nextIsLastToken = false;
public MyTokenizer(String s, String delim) {
this.s = s;
this.delim = delim;
}
public Iterator<String> iterator() {
return this;
}
public boolean hasNext() {
nextIndex = s.indexOf(delim, curIndex);
if (nextIsLastToken)
return false;
if (nextIndex == -1)
nextIsLastToken = true;
return true;
}
public String next() {
if (nextIndex == -1)
nextIndex = s.length();
String token = s.substring(curIndex, nextIndex);
curIndex = nextIndex + 1;
return token;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
结果
测试使用Java SE 6(build 1.6.0_12-b04)运行,结果如下:
long st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
StringTokenizer t = new StringTokenizer("dog,,cat", ",");
while (t.hasMoreTokens()) {
t.nextToken();
}
}
System.out.println(System.currentTimeMillis() - st);
st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
MyTokenizer mt = new MyTokenizer("dog,,cat", ",");
for (String t : mt) {
}
}
System.out.println(System.currentTimeMillis() - st);
st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
String[] tokens = "dog,,cat".split(",");
for (String t : tokens) {
}
}
System.out.println(System.currentTimeMillis() - st);
st = System.currentTimeMillis();
Pattern p = Pattern.compile(",");
for (int i = 0; i < 1e6; i++) {
String[] tokens = p.split("dog,,cat");
for (String t : tokens) {
}
}
System.out.println(System.currentTimeMillis() - st);
Run 1 Run 2 Run 3 Run 4 Run 5
----- ----- ----- ----- -----
StringTokenizer 172 188 187 172 172
MyTokenizer 234 234 235 234 235
String.split 1172 1156 1171 1172 1156
Pattern.compile 906 891 891 907 906
运行1运行2运行3
Run 1 Run 2 Run 3 Run 4 Run 5
----- ----- ----- ----- -----
StringTokenizer 172 188 187 172 172
MyTokenizer 234 234 235 234 235
String.split 1172 1156 1171 1172 1156
Pattern.compile 906 891 891 907 906