鼓励通过 , 事实上我有数十亿的串的分析,我试图修改我的代码接受 StringTokenizer 而不是的 String[]

唯一剩下的东西和我之间得到那美味的x2性能的提高是事实当你做

"dog,,cat".split(",")
//output: ["dog","","cat"]

StringTokenizer("dog,,cat")
// nextToken() = "dog"
// nextToken() = "cat"

我如何可以实现类似的结果与StringTokenizer?有没有更快的方式这样做?

有帮助吗?

解决方案

您实际上只标化的逗号?如果是的话,我会写我自己的标记生成器 - 它很可能最终会被甚至比更通用的StringTokenizer将可以寻找多个令牌更高效,你可以把它的行为,但是你会喜欢。这样一个简单的使用情况中,它可以是一个简单的实现。

如果它是有用的,你甚至可以实现Iterable<String>并获得增强的for循环支持,强类型,而不是由Enumeration提供的StringTokenizer支持。让我知道如果你想要的任何帮助编码这样的野兽起来 - 它真的不应该太难

此外,我想尝试从现有的解决方案跨越太远之前运行的实际数据的性能测试。你有任何想法如何你的执行时间是多少的实际的在String.split度过?我知道你有很多字符串的解析,但如果你以后做什么显著和他们在一起,我期望这是远远超过分裂更显著。

其他提示

修补之后 StringTokenizer 类,我找不到满足返回要求的方法 ["dog", "", "cat"].

此外, StringTokenizer 仅出于兼容性原因保留类,并且使用 String.split 受到鼓励。来自 API 规范 StringTokenizer:

StringTokenizer 是一个遗留类,由于兼容原因而被保留,尽管它在新代码中不建议使用。建议任何寻求此功能的人使用 split 的方法 String 或者 java.util.regex包代替。

由于问题是据称性能不佳 String.split 方法,我们需要找到替代方法。

笔记:我说“据说性能很差”,因为很难确定每个用例都会导致 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();
  }
}

MyTokenizer 将采取 String 标记化和 String 作为分隔符,并使用 String.indexOf 方法来执行分隔符搜索。代币是由 String.substring 方法。

我怀疑通过在字符串上工作可能会提高一些性能 char[] 水平而不是在 String 等级。但我会将其作为练习留给读者。

该类还实现了 IterableIterator 为了利用 for-each Java 5 中引入的循环构造。 StringTokenizer 是一个 Enumerator, ,并且不支持 for-each 构造。

是不是更快一点了?

为了看看这是否更快,我编写了一个程序来比较以下四种方法的速度:

  1. 用于 StringTokenizer.
  2. 使用新的 MyTokenizer.
  3. 用于 String.split.
  4. 使用预编译的正则表达式 Pattern.compile.

在这四种方法中,字符串 "dog,,cat" 被分成令牌。虽然 StringTokenizer 包含在比较中,需要注意的是,它不会返回期望的结果 ["dog", "", "cat].

标记化总共重复了 100 万次,以便有足够的时间来注意到方法的差异。

用于简单基准测试的代码如下:

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);

结果

测试使用 Java SE 6(版本 1.6.0_12-b04)运行,结果如下:

                   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

因此,从有限的测试和仅五次运行中可以看出, StringTokenizer 事实上确实是最快的,但是 MyTokenizer 紧随其后位列第二。然后, String.split 最慢,预编译的正则表达式比预编译的正则表达式稍快 split 方法。

与任何小基准一样,它可能不能很好地代表现实生活中的情况,因此对结果应该持保留态度。

注:具有过一些快速基准扫描结果是大约四倍的速度慢于串。分裂。因此,不要使用扫描仪。

(我离开后为记录的事实,扫描仪是一个糟糕的想法,在这种情况。(读作:不列表对我说的扫描仪,请...))

假设你正在使用Java1.5或更高时,尝试 扫描仪, ,这实现了 Iterator<String>, ,因为它发生:

Scanner sc = new Scanner("dog,,cat");
sc.useDelimiter(",");
while (sc.hasNext()) {
    System.out.println(sc.next());
}

给:

dog

cat

根据什么样的,你需要来标记字符串,你可以写(),例如基于String.indexOf自己分离器。您还可以创建一个多核心解决方案,以进一步提高性能,作为字符串的标记化是相互独立的。在-lets声言如下─每核心100个字符串的批次工作。做String.split()或其他watever

而不是StringTokenizer的,你可以从Apache的Commons Lang中,我引用尝试StrTokenizer类

  

此类可以拆分成字符串许多较小的字符串。它的目的是做类似的工作,StringTokenizer的,但它提供了更多的控制和灵活性,包括实施的ListIterator接口。

     

空令牌可以被删除或返回空。

这听起来像你需要什么,我认为?

您可以做这样的事情。它并不完美,但它可能为你工作。

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;
}

如果可能,你可以ommit名单的事情,直接做一些子串:

public static void split(String test, char c) {
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        String s = test.substring(start,i);
         // do something with the string here
        i++;
    }
}

在我的系统的最后一个方法比StringTokenizer的解决方案更快,但你可能要测试它是如何为你工作。 (当然你可以通过ommiting使这一方法缩短了一点{}第二,而外观,当然你可以使用一个for循环,而不是外部while循环,并包括最后我++到这一点,但我没有”做T,在这里,因为我认为不好的风格。

好了,你可以做的跑得最快的家伙是手动遍历字符串,例如

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;
    }

此(非正式的试验)看起来是这样的快两倍分裂。然而,这是一个有点危险迭代这种方式,例如,它会在逃脱逗号打破,如果你最终需要在某些时候,处理的是(因为你的十亿字符串列表中有3逃脱逗号)你的时候“已经允许它,你可能会最终失去了一些速度益处的。

最终它可能不值得费心。

我建议谷歌的番石榴Splitter。结果 我 coobird 检验比较,并得到以下结果:

  

的StringTokenizer 104,点击   谷歌番石榴分配器142结果   String.split 446结果   正则表达式299

如果您输入的结构,你可以看看JavaCC的编译器。它生成一个Java类阅读您的输入。它是这样的:

TOKEN { <CAT: "cat"> , <DOG:"gog"> }

input: (cat() | dog())*


cat: <CAT>
   {
   animals.add(new Animal("Cat"));
   }

dog: <DOG>
   {
   animals.add(new Animal("Dog"));
   }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top