답변:
그들은 본질적으로 코스의 말입니다.
Scanner
문자열을 구문 분석하고 다른 유형의 데이터를 가져와야하는 경우를 위해 설계되었습니다. 매우 유연하지만 특정 표현식으로 구분 된 문자열 배열을 얻는 가장 간단한 API를 제공하지는 않습니다.String.split()
그리고 Pattern.split()
당신이 후자를 수행하기위한 쉬운 구문을 제공하지만 본질적으로 그들은 모두 할 것이 있습니다. 결과 문자열을 구문 분석하거나 특정 토큰에 따라 구분 기호를 반쯤 변경하려는 경우 도움이되지 않습니다.StringTokenizer
보다 제한 String.split()
적이고 사용하기가 조금 더 복잡합니다. 기본적으로 고정 하위 문자열로 구분 된 토큰을 가져 오기 위해 설계되었습니다. 이 제한으로 인해 약 2 배 빠릅니다 String.split()
. ( 및 비교를String.split()
StringTokenizer
참조하십시오 .) 또한 정규 표현식 API보다 우선합니다 String.split()
.필자 는 일반적인 머신에서 몇 밀리 초 안에 수천 개의 문자열을String.split()
토큰 화 할 수 있다는 타이밍을 알 수 있습니다 . 또한 출력을 문자열 배열로 제공하는 것 보다 장점 이 있습니다. 이는 일반적으로 원하는 것입니다. 에서 제공 한대로을 사용하는 것은 대부분 "구문 적으로 까다로워"입니다. 이 관점에서 요즘은 약간의 공간 낭비이며,을 사용할 수도 있습니다 .StringTokenizer
Enumeration
StringTokenizer
StringTokenizer
String.split()
StringTokenizer
하기 때문에 여전히 최선의 방법 String.split()
이 아닌가?
를 제거하여 시작하겠습니다 StringTokenizer
. 나이가 들었고 정규 표현식조차 지원하지 않습니다. 설명서에는 다음과 같은 내용이 있습니다.
StringTokenizer
새 코드에서는 사용을 권장하지 않지만 호환성을 위해 유지되는 레거시 클래스입니다. 이 기능을 원하는 사람은split
방법String
이나java.util.regex
패키지를 대신 사용하는 것이 좋습니다 .
이제 바로 버려 봅시다. 그 나뭇잎 split()
과 Scanner
. 그들 사이의 차이점은 무엇입니까?
우선, split()
배열을 반환하면 foreach 루프를 쉽게 사용할 수 있습니다.
for (String token : input.split("\\s+") { ... }
Scanner
더 스트림처럼 만들어집니다.
while (myScanner.hasNext()) {
String token = myScanner.next();
...
}
또는
while (myScanner.hasNextDouble()) {
double token = myScanner.nextDouble();
...
}
(이것은 다소 큰 API 를 가지고 있으므로 항상 간단한 것으로 제한되지 않는다고 생각하십시오.)
이 스트림 스타일 인터페이스는 구문 분석을 시작하기 전에 모든 입력이 없거나 가져올 수없는 경우 간단한 텍스트 파일 또는 콘솔 입력을 구문 분석하는 데 유용 할 수 있습니다.
개인적으로, 내가 기억할 수있는 유일한 시간 Scanner
은 명령 줄에서 사용자 입력을 얻어야 할 때 학교 프로젝트를 위해서입니다. 그것은 그런 종류의 작업을 쉽게 만듭니다. 그러나 내가 String
나누고 싶은 것을 가지고 있다면 , 거의 갈 필요가 없습니다 split()
.
Scanner
주어진 줄 바꿈 문자를 감지했습니다 String
. 새 라인 문자 (봐 플랫폼에 따라 다를 수 있기 때문에 Pattern
!의 자바 독을) 및 입력 문자열을 준수하도록 보장되지 않습니다 System.lineSeparator()
, 나는 찾을 Scanner
이미 새로운 라인 문자를 호출 할 때 무엇을 찾아야할지 알고로 더 적합합니다 nextLine()
. 왜냐하면 String.split
올바른 표준 표현식 패턴을 입력하여 줄 구분 기호를 감지해야합니다. 줄 구분 기호는 표준 위치에 저장되어 있지 않습니다 ( Scanner
클래스 소스 에서 복사하는 것이 가장 좋습니다 ).
StringTokenizer는 항상 존재했습니다. 가장 빠르지 만 열거 형 관용구가 다른 것만 큼 우아하지 않을 수 있습니다.
JDK 1.4에서 split이 존재하게되었습니다. 토크 나이저보다 느리지 만 String 클래스에서 호출 할 수 있으므로 사용하기가 더 쉽습니다.
스캐너는 JDK 1.5에있게되었습니다. 가장 유연하고 유명한 Cs scanf 함수 제품군과 동등한 기능을 지원하기 위해 Java API에서 오랜 간격을 메 웁니다.
토큰 화하려는 String 객체가있는 경우 StringTokenizer보다 String의 split 메소드를 사용하는 것이 좋습니다. 파일이나 사용자와 같이 프로그램 외부의 소스에서 텍스트 데이터를 구문 분석하는 경우 검사기가 편리합니다.
분할 속도는 느리지 만 스캐너만큼 느리지는 않습니다. StringTokenizer가 분할보다 빠릅니다. 그러나 JFastParser https://github.com/hughperkins/jfastparser 에서 속도 부스트를 얻기 위해 유연성을 교환하여 속도를 두 배로 늘릴 수 있음을 알았습니다.
백만 배가 포함 된 문자열 테스트 :
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
String.split은 StringTokenizer보다 훨씬 느린 것 같습니다. split의 유일한 장점은 토큰 배열을 얻는 것입니다. 또한 정규식을 분할하여 사용할 수 있습니다. org.apache.commons.lang.StringUtils에는 두 개의 viz보다 훨씬 빠르게 작동하는 split 메소드가 있습니다. StringTokenizer 또는 String.split. 그러나 세 가지 모두에 대한 CPU 사용률은 거의 동일합니다. 따라서 CPU를 덜 사용하는 방법이 필요하지만 여전히 찾을 수 없습니다.
나는 최근에 성능에 민감한 상황에서 String.split ()의 나쁜 성능에 대한 실험을했습니다. 이 기능이 유용 할 수 있습니다.
http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr
요점은 String.split ()이 매번 정규 표현식 패턴을 컴파일하므로 미리 컴파일 된 Pattern 객체를 사용하고이를 직접 사용하여 String에서 작동하는 경우에 비해 프로그램 속도가 느려질 수 있다는 것입니다.
기본 시나리오의 경우 Pattern.split ()을 제안하지만 최대 성능이 필요한 경우 (특히 Android에서 테스트 한 모든 솔루션이 매우 느림) 단일 문자로만 분할하면 이제 내 자신의 방법을 사용합니다.
public static ArrayList<String> splitBySingleChar(final char[] s,
final char splitChar) {
final ArrayList<String> result = new ArrayList<String>();
final int length = s.length;
int offset = 0;
int count = 0;
for (int i = 0; i < length; i++) {
if (s[i] == splitChar) {
if (count > 0) {
result.add(new String(s, offset, count));
}
offset = i + 1;
count = 0;
} else {
count++;
}
}
if (count > 0) {
result.add(new String(s, offset, count));
}
return result;
}
"abc".toCharArray ()를 사용하여 문자열의 문자 배열을 가져옵니다. 예를 들면 다음과 같습니다.
String s = " a bb ccc dddd eeeee ffffff ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');
한 가지 중요한 차이점은 String.split ()과 Scanner가 모두 빈 문자열을 생성 할 수 있지만 StringTokenizer는이를 수행하지 않는다는 것입니다.
예를 들면 다음과 같습니다.
String str = "ab cd ef";
StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());
String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);
Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());
산출:
//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2:
#3: ef
//Scanner
#0: ab
#1: cd
#2:
#3: ef
String.split () 및 Scanner.useDelimiter ()의 구분 기호는 단순한 문자열이 아니라 정규식이기 때문입니다. 위의 예에서 구분 기호 ""를 "+"로 바꾸면 StringTokenizer처럼 동작 할 수 있습니다.