문제

격려 이것, 그리고 구문 분석 할 수십억 개의 문자열이 있다는 사실은 수락 할 코드를 수정하려고했습니다. StringTokenizer 대신에 끈[

나 사이에 남겨진 유일한 것은 맛있는 X2 성능 향상을 얻는 것은 당신이 할 때

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

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

StringTokenizer와 비슷한 결과를 얻을 수있는 방법은 무엇입니까? 이것을하는 더 빠른 방법이 있습니까?

도움이 되었습니까?

해결책

당신은 실제로 쉼표에서만 토큰 화되고 있습니까? 그렇다면, 나는 내 자신의 토큰 화기를 쓸 것입니다 - 그것은 여러 토큰을 찾을 수있는 더 범용 StringTokenizer보다 훨씬 더 효율적 일 수 있으며, 원하는대로 행동 할 수 있습니다. 이러한 간단한 사용 사례의 경우 간단한 구현이 될 수 있습니다.

유용하면 구현할 수도 있습니다. Iterable<String> 그리고 대신 강력한 타이핑으로 루프 지원을 강화하십시오. Enumeration 지원 StringTokenizer. 그런 짐승을 코딩하는 데 도움이 되시면 알려주십시오. 정말 어렵지 않아야합니다.

또한 기존 솔루션에서 너무 멀리 도약하기 전에 실제 데이터에서 성능 테스트를 실행하려고합니다. 실행 시간이 얼마나되는지 아십니까? 실제로 보냈다 String.split? 나는 당신이 구문 분석 할 끈이 많다는 것을 알고 있지만, 나중에 중요한 일을하고 있다면 분할보다 훨씬 더 중요 할 것으로 기대합니다.

다른 팁

THE와 함께 StringTokenizer 수업, 나는 반환 할 요구 사항을 충족시키는 방법을 찾을 수 없었다. ["dog", "", "cat"].

또한, StringTokenizer 클래스는 호환성 이유와 사용에 대해서만 남겨 둡니다. String.split 암호화됩니다. API 사양에서 StringTokenizer:

StringTokenizer 새로운 코드에서 사용이 권장되지만 호환성 이유로 유지되는 레거시 클래스입니다. 이 기능을 원하는 사람은 누구나 사용하는 것이 좋습니다. split 의 방법 String 아니면 그 java.util.regex대신 패키지.

문제는 아마도 String.split 방법, 대안을 찾아야합니다.

참고 : 나는 모든 유스 케이스가 StringTokenizer 보다 우수합니다 String.split 방법. 또한, 많은 경우에, 문자열의 토큰 화가 실제로 적절한 프로파일 링에 의해 결정된 응용 프로그램의 병목 현상이 아니라면, 그것이 조기 최적화가 될 것이라고 생각합니다. 나는 최적화에 대한 환기를하기 전에 의미 있고 이해하기 쉬운 글쓰기 코드를 말하는 경향이 있습니다.

이제 현재 요구 사항에서 아마도 우리 자신의 토큰 화기를 굴리는 것은 그리 어렵지 않을 것입니다.

우리 자신의 토켄 지어를 굴립니다!

다음은 내가 쓴 간단한 토 케이저입니다. 속도 최적화가 없으며 문자열의 끝을 지나가는 것을 방지 할 오류 확인도 없음에 주목해야합니다. 이것은 빠르고 더 큰 구현입니다.

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 a String 토큰 화 및 a String 구분자로서 사용하십시오 String.indexOf 구분 제에 대한 검색을 수행하는 방법. 토큰이 생산합니다 String.substring 방법.

나는 문자열에서 작업함으로써 성능 향상이있을 수 있다고 생각합니다. char[] 그보다는 레벨 String 수준. 그러나 나는 그것을 독자들에게 운동으로 남겨 둘 것입니다.

수업도 구현됩니다 Iterable 그리고 Iterator 그것을 활용하기 위해 for-each Java 5에 도입 된 루프 구조. StringTokenizer 이다 Enumerator, 그리고 지원하지 않습니다 for-each 건설하다.

더 빠른가요?

이것이 더 빠른지 알아 내기 위해 다음 네 가지 방법의 속도를 비교하는 프로그램을 작성했습니다.

  1. 사용 StringTokenizer.
  2. 새로운 사용 MyTokenizer.
  3. 사용 String.split.
  4. 사전 컴파일 된 정규 표현 사용 Pattern.compile.

네 가지 방법에서는 문자열입니다 "dog,,cat" 토큰으로 분리되었습니다. 비록 StringTokenizer 비교에 포함되며 원하는 결과를 반환하지 않을 것입니다. ["dog", "", "cat].

토큰 화는 총 1 백만 번 동안 반복되어 방법의 차이를 알아 차릴 수있는 충분한 시간이 걸렸습니다.

간단한 벤치 마크에 사용되는 코드는 다음과 같습니다.

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

따라서 제한된 테스트와 5 번의 실행에서 볼 수 있듯이 StringTokenizer 실제로 가장 빨리 나왔지만 MyTokenizer 가까운 2 위로 들어 왔습니다. 그 다음에, String.split 가장 느 렸고 사전 컴파일 된 정규 표현은 split 방법.

작은 벤치 마크와 마찬가지로 실제 조건을 대표하지는 않을 것입니다. 따라서 결과는 곡물 (또는 마운드)의 소금으로 촬영해야합니다.

참고 : 몇 가지 빠른 벤치 마크를 수행 한 스캐너는 String.split보다 약 4 배 느린 것으로 판명되었습니다. 따라서 스캐너를 사용하지 마십시오.

(이 경우 스캐너가 나쁜 생각이라는 사실을 기록하기 위해 게시물을 떠나고 있습니다.

Java 1.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 () 또는 다른 wavorite를 수행하십시오.

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-Solution보다 빠르지 만 그것이 어떻게 작동하는지 테스트 할 수 있습니다. (물론 당신은 두 번째 룩의 {}를 {}를 조금 더 짧게 만들 수 있으며 물론 외부 대신 루프 대신에 for-loop를 사용할 수 있고 마지막 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;
    }

이 (비공식 테스트)는 분할의 두 배나 빠른 것 같습니다. 그러나 예를 들어 탈출 한 쉼표에서 깨지는 것과 같이 이런 식으로 반복하는 것은 약간 위험합니다. 만약 당신이 어느 시점에서 그것을 다루어야한다면 (당신의 당신의 당신의 당신의 목록에 당신의 당신의 목록은 3 개의 탈출 된 쉼표가 있기 때문에) 당신은 당신입니다. 'VE는 그것을 허용했을 것입니다.

궁극적으로 그것은 아마도 귀찮게 할 가치가 없을 것입니다.

Google의 Guava를 추천합니다 Splitter.
나는 그것을 비교했다 Coobird 테스트하고 다음과 같은 결과를 얻었습니다.

StringTokenizer 104
Google Guava Splitter 142
문자열 .split 446
Regexp 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