[java] Java에서 문자열의 문자를 반복하는 가장 쉽고 가장 좋은 방법은 무엇입니까?

StringTokenizer? 변환 StringA와 char[]그 이상과 반복 처리? 다른 것?



답변

for 루프를 사용하여 문자열을 반복하고 charAt()각 문자를 검사하도록합니다. 문자열은 배열로 구현되므로이 charAt()메서드는 일정한 시간 작업입니다.

String s = "...stuff...";

for (int i = 0; i < s.length(); i++){
    char c = s.charAt(i);        
    //Process char
}

그게 내가 할 것입니다. 가장 쉬운 것 같습니다.

정확성에 관한 한, 나는 그것이 여기에 있다고 믿지 않습니다. 그것은 모두 당신의 개인적인 스타일에 근거합니다.


답변

두 가지 옵션

for(int i = 0, n = s.length() ; i < n ; i++) { 
    char c = s.charAt(i); 
}

또는

for(char c : s.toCharArray()) {
    // process c
}

첫 번째는 아마도 더 빠르며, 두 번째는 더 읽기 쉽습니다.


답변

BMP (Unicode Basic Multilingual Plane ) 외부의 문자 , 즉 u0000-uFFFF 범위를 벗어난 코드 포인트 를 처리하는 경우 여기에 설명 된 다른 기술 대부분이 분류됩니다 . 외부의 코드 포인트는 대부분 죽은 언어에 할당되므로 거의 발생하지 않습니다. 그러나이 밖에는 유용한 표기법이 있습니다. 예를 들어 수학 표기법에 사용되는 코드 포인트와 중국어로 적절한 이름을 인코딩하는 데 사용되는 코드 포인트가 있습니다.

이 경우 코드는 다음과 같습니다.

String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
  int curChar = str.codePointAt(offset);
  offset += Character.charCount(curChar);
  // do something with curChar
}

Character.charCount(int)방법에는 Java 5 이상이 필요합니다.

출처 : http://mindprod.com/jgloss/codepoint.html


답변

StringTokenizer가 여기서 과도하게 사용된다는 데 동의합니다. 실제로 위의 제안을 시도하고 시간이 걸렸습니다.

내 테스트는 매우 간단했습니다. 약 백만 개의 문자로 StringBuilder를 만들고 문자열로 변환 한 다음 charAt () / char 배열로 변환 한 후 / CharacterIterator를 사용하여 천 번 (각각 컴파일러가 전체 루프를 최적화하지 못하도록 문자열에서 무언가를 수행하십시오 :-)).

내 2.6 GHz Powerbook (맥 :-)) 및 JDK 1.5의 결과 :

  • 테스트 1 : charAt + String-> 3138msec
  • 테스트 2 : 문자열을 배열로 변환-> 9568msec
  • 테스트 3 : StringBuilder charAt-> 3536msec
  • 테스트 4 : CharacterIterator 및 문자열-> 12151msec

결과가 크게 다르기 때문에 가장 간단한 방법도 가장 빠른 것 같습니다. 흥미롭게도 StringBuilder의 charAt ()는 String보다 약간 느립니다.

BTW 나는 ‘\ uFFFF’문자의 남용을 “반복의 끝”으로 간주하여 CharacterIterator를 사용하지 않는 것이 좋습니다. 큰 프로젝트에는 항상 두 종류의 서로 다른 목적으로 같은 종류의 핵을 사용하는 두 사람이 있으며 코드는 실제로 신비하게 충돌합니다.

테스트 중 하나는 다음과 같습니다.

    int count = 1000;
    ...

    System.out.println("Test 1: charAt + String");
    long t = System.currentTimeMillis();
    int sum=0;
    for (int i=0; i<count; i++) {
        int len = str.length();
        for (int j=0; j<len; j++) {
            if (str.charAt(j) == 'b')
                sum = sum + 1;
        }
    }
    t = System.currentTimeMillis()-t;
    System.out.println("result: "+ sum + " after " + t + "msec");


답변

Java 8 에서는 다음과 같이 해결할 수 있습니다.

String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));

chars () 메소드 IntStreamdoc 에서 언급 한대로를 반환합니다 .

이 순서로부터 char 값을 제로 확장하는 int의 스트림을 돌려줍니다. 대리 코드 포인트에 매핑되는 모든 문자는 해석되지 않은 상태로 전달됩니다. 스트림을 읽는 동안 시퀀스가 ​​변경되면 결과가 정의되지 않습니다.

이 메소드 codePoints()는 또한 IntStream문서마다를 반환합니다 .

이 순서로부터 코드 포인트 치의 스트림을 돌려줍니다. 시퀀스에서 발생하는 대리 쌍은 Character.toCodePoint에 의해 결합되고 결과가 스트림으로 전달됩니다. 일반 BMP 문자, 쌍을 이루지 않은 서로 게이트 및 정의되지 않은 코드 단위를 포함한 다른 코드 단위는 int 값으로 0 확장되어 스트림으로 전달됩니다.

문자와 코드 포인트는 어떻게 다릅니 까? 기사 에서 언급했듯이 :

유니 코드 3.1은 보충 문자를 추가하여 단일 문자 16 비트로 구별 할 수있는 216 자 이상의 총 문자 수를 가져옵니다 char. 따라서 char값이 더 이상 유니 코드의 기본 의미 단위에 일대일로 맵핑되지 않습니다. JDK 5는 더 큰 문자 값 세트를 지원하도록 업데이트되었습니다. char유형 의 정의를 변경하는 대신 새로운 보충 문자 중 일부는 두 char값 의 서로 게이트 쌍으로 표시됩니다 . 이름 혼동을 줄이기 위해 코드 포인트는 보충 문자를 포함하여 특정 유니 코드 문자를 나타내는 숫자를 나타내는 데 사용됩니다.

마지막으로 왜 forEachOrdered그렇지 forEach않습니까?

의 동작이 forEach명확하게 결정적되는 위치로서 forEachOrdered수행이 스트림의 각 요소에 대한 액션, 스트림의 발생 순서 스트림 정의 발생 순서를 갖는 경우. 따라서 forEach주문이 유지된다는 보장은 없습니다. 또한이 질문 을 확인하십시오 .

문자, 코드 포인트, 글리프 (glyph) 및 그래 핀 (grapheme)의 차이점에 대해서는 이 질문을 확인하십시오 .


답변

이를위한 몇 가지 전용 클래스가 있습니다.

import java.text.*;

final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
   // process c
   ...
}


답변

당신이있는 경우 구아바을 클래스 패스에 다음 꽤 읽을 수있는 대안이다. 구아바는이 경우 상당히 합리적인 사용자 정의 목록 구현을 가지고 있기 때문에 비효율적이지 않아야합니다.

for(char c : Lists.charactersOf(yourString)) {
    // Do whatever you want     
}

업데이트 : @Alex가 지적했듯이 Java 8에서도 CharSequence#chars사용해야합니다. 유형조차도 IntStream이므로 다음과 같은 문자로 매핑 할 수 있습니다.

yourString.chars()
        .mapToObj(c -> Character.valueOf((char) c))
        .forEach(c -> System.out.println(c)); // Or whatever you want