학습 기록/알고리즘

StringTokenizer VS String.split()

당중독자 카코 2023. 9. 20. 00:53

https://bird-liver-ecc.notion.site/StringTokenizer-VS-String-split-7133dade7c804f46a48994859fc514b0?pvs=4

 

StringTokenizer VS String.split()

StringTokenizer 와 String.split() 의 속도 차이와 그 이유

bird-liver-ecc.notion.site

↑ 원본링크 

 

 

- 참조 -

StringTokenizer 와 String.split() 의 속도 차이와 그 이유

StringTokenizer는 느릴까? 실험하기

StringTokenizer 관련 StackOverflow 게시글

 

 

무엇이 더 빠른가? 🏃‍♂️

  • 알고리즘 문제를 풀면서 각종 해설에서 StringTokenizer를 사용한다. 그래서 당연히 String.split() 은 속도를 생각하면 사용하면 안되는 메소드로 생각하고 있었다. 그런데 생각보다 그렇게 단순히 결정짓고 넘어갈 문제가 아니었다.
  • 결론 :
    1. 그때그때 다르다.
    2. StringTokenizer는 레거시코드다. (deprecated 되지는 않았다)
    3. String.split() 은 비교적 일정한 속도를 내지만 StringTokenizer는 경우에 따라 편차가 크다.
    4. 즉 상황에 맞춰서 써라. ( 자바 버전을 업그레이드 했을 때 문제 생길지는 미지수 jdk17까지는 사용할 수 있는듯 )

속도 차이가 왜 생기나? 🤷‍♂️

  • String.split()은 정규표현식을 사용해 문자열을 나누고 이는 속도가 느린 원인이 된다.
    • 실제로 알고리즘 문제를 풀다보면 속도차이가 꽤 생기는 경우 발생
    • 하지만 속도가 꽤나 균일하다
  • StringTokenizer는 구분자(delimeter)와 문자열을 전부 다 비교한다.
  • StringTokenizer는 ‘구분자가 유니코드, hasMoreTokens나 nextToken 호출’ 시에도 문자열과 구분자 전체를 비교하기 때문에 효율이 좋지 못하다.
    • 즉 위에서 말한 조건이 만족되는 경우가 많을수록 속도가 급속도로 느려진다.
    • 예를들어… 아스키코드에 존재하지 않는 “뷁” 같은 문자를 구분자로 사용하며 그 수가 많고, 여러 문자를 나누어 hasMoreTokens()나 nextToken() 을 반복해서 사용한다면 StringTokenizer를 사용하는건 좋은 선택이 아니다.

실험 결과 비교하기

  1. 첫번째 실험 : 구분자를 “,” 사용 / hasMoreTokens() 사용 안함 / nextToken() 한번 사용
    1. 결과 :
    splits ====== time : 271, clientIp : 192.168.1.1
    tokenizer ====== time : 80, clientIp : 192.168.1.1
    [출처] [StringTokenizer VS String.split] 누가 더 빠른가|작성자 평범한개발자
    
  2. 두번째 실험 : 구분자를 아스키코드가 아닌 문자 사용 / hasMoreTokens(), nextToken() 반복 사용
    1. 코드 :
    StringTokenizer 와 String.split() 의 속도 차이와 그 이유b. 결과 : 차이가 갑자기 확 좁혀졌다
  3. splits ====== time : 257, clientIp : 127.0.0.3 tokenizer ====== time : 220, clientIp : 127.0.0.3 [출처] [StringTokenizer VS String.split] 누가 더 빠른가|작성자 평범한개발자
  4. // 위 링크에서 코드 발췌 @Test public void getClientIpSplitTest() { String clientIp = null; long start = System.currentTimeMillis(); for(int i = 0; i < 1000000; i++) { String xForwardedFor="192.168.1.1뛟127.0.0.1뛟127.0.0.2뛟127.0.0.3"; String[] splits = xForwardedFor.split("뛟"); if(splits.length < 1) clientIp = ""; else { for(String str : splits) { clientIp = str; } } } long end = System.currentTimeMillis(); log.info("splits ====== time : {}, clientIp : {}", end-start, clientIp); } @Test public void getClientIpStringTokenizerTest() { String clientIp = null; long start = System.currentTimeMillis(); for(int i = 0; i < 1000000; i++) { String xForwardedFor="192.168.1.1뛟127.0.0.1뛟127.0.0.2뛟127.0.0.3"; StringTokenizer tokenizer = new StringTokenizer(xForwardedFor, "뛟"); while(tokenizer.hasMoreTokens()) { clientIp = tokenizer.nextToken(); } if(clientIp == null) clientIp = ""; } long end = System.currentTimeMillis(); log.info("tokenizer ====== time : {}, clientIp : {}", end-start, clientIp); } [출처] [StringTokenizer VS String.split] 누가 더 빠른가|작성자 평범한개발자
  5. 세번째 실험 : 두번째 실험에서 구분자 “하나” 추가 / hasMoreTokens(), nextToken() 반복 사용
    1. 결과 : 역전 당했다. 효율이 정말 좋지 않아보인다.
    "192.168.1.1뛟127.0.0.1뛟127.0.0.2뛟127.0.0.3뛟127.0.0.4";
    
    splits ====== time : 270, clientIp : 127.0.0.4
    tokenizer ====== time : 319, clientIp : 127.0.0.4 
    [출처] [StringTokenizer VS String.split] 누가 더 빠른가|작성자 평범한개발자