StringTokenizer
? 변환 String
A와 char[]
그 이상과 반복 처리? 다른 것?
StringTokenizer
? 변환 String
A와 char[]
그 이상과 반복 처리? 다른 것?
답변:
for 루프를 사용하여 문자열을 반복하고 charAt()
각 문자를 검사하도록합니다. 문자열은 배열로 구현되므로이 charAt()
메서드는 일정한 시간 작업입니다.
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
그게 내가 할 것입니다. 가장 쉬운 것 같습니다.
정확성에 관한 한, 나는 그것이 여기에 있다고 믿지 않습니다. 그것은 모두 당신의 개인적인 스타일에 근거합니다.
String.charAt(int)
는 단지하고 value[index]
있습니다. chatAt()
코드 포인트를 제공하는 다른 것과 혼동 하고 있다고 생각합니다 .
두 가지 옵션
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 이상이 필요합니다.
StringTokenizer가 여기서 과도하게 사용된다는 데 동의합니다. 실제로 위의 제안을 시도하고 시간이 걸렸습니다.
내 테스트는 매우 간단했습니다. 약 백만 개의 문자로 StringBuilder를 만들고 문자열로 변환 한 다음 charAt () / char 배열로 변환 한 후 / CharacterIterator를 사용하여 천 번 (각각 컴파일러가 전체 루프를 최적화하지 못하도록 문자열에서 무언가를 수행하십시오 :-)).
내 2.6 GHz Powerbook (맥 :-)) 및 JDK 1.5의 결과 :
결과가 크게 다르기 때문에 가장 간단한 방법도 가장 빠른 것 같습니다. 흥미롭게도 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 () 메소드 IntStream
는 doc 에서 언급 한대로를 반환합니다 .
이 순서로부터 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
...
}
char
제공 하는 것보다 많은 공간을 필요로하기 때문에 문자 반복자를 사용하는 것이 문자를 반복하는 유일한 올바른 방법 일 것입니다 . Java char
는 16 비트를 포함하며 U + FFFF의 유니 코드 문자를 보유 할 수 있지만 유니 코드는 U + 10FFFF의 문자를 지정합니다. 16 비트를 사용하여 유니 코드를 인코딩하면 가변 길이 문자 인코딩이 발생합니다. 이 페이지의 대부분의 답변은 Java 인코딩이 일정한 길이의 인코딩이라고 가정합니다.
당신이있는 경우 구아바을 클래스 패스에 다음 꽤 읽을 수있는 대안이다. 구아바는이 경우 상당히 합리적인 사용자 정의 목록 구현을 가지고 있기 때문에 비효율적이지 않아야합니다.
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
코드 포인트를 반복 해야하는 경우 String
(이 답변 참조 ) 더 짧고 읽기 쉬운 방법은 CharSequence#codePoints
Java 8에 추가 된 메소드 를 사용하는 것입니다.
for(int c : string.codePoints().toArray()){
...
}
또는 for 루프 대신 스트림을 직접 사용하십시오.
string.codePoints().forEach(c -> ...);
도 있습니다 CharSequence#chars
(그것이 비록 당신이 문자의 스트림을 원하지 않는 경우 IntStream
에는이 있으므로, CharStream
).
StringTokenizer
JDK의 클래스 중 하나이기 때문에 사용하지 않을 것 입니다.
javadoc은 말합니다 :
StringTokenizer
새 코드에서는 사용을 권장하지 않지만 호환성 이유로 유지되는 레거시 클래스입니다. 이 기능을 원하는 사람은 split 방법String
이나java.util.regex
패키지를 대신 사용하는 것이 좋습니다 .
성능이 필요한 경우 환경을 테스트 해야합니다. 다른 방법은 없습니다.
예제 코드는 다음과 같습니다.
int tmp = 0;
String s = new String(new byte[64*1024]);
{
long st = System.nanoTime();
for(int i = 0, n = s.length(); i < n; i++) {
tmp += s.charAt(i);
}
st = System.nanoTime() - st;
System.out.println("1 " + st);
}
{
long st = System.nanoTime();
char[] ch = s.toCharArray();
for(int i = 0, n = ch.length; i < n; i++) {
tmp += ch[i];
}
st = System.nanoTime() - st;
System.out.println("2 " + st);
}
{
long st = System.nanoTime();
for(char c : s.toCharArray()) {
tmp += c;
}
st = System.nanoTime() - st;
System.out.println("3 " + st);
}
System.out.println("" + tmp);
에 자바 온라인 내가 얻을 :
1 10349420
2 526130
3 484200
0
Android x86 API 17에서 다음을 얻습니다.
1 9122107
2 13486911
3 12700778
0
Java 학습서 : 문자열을 참조하십시오 .
public class StringDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
char[] tempCharArray = new char[len];
char[] charArray = new char[len];
// put original string in an array of chars
for (int i = 0; i < len; i++) {
tempCharArray[i] = palindrome.charAt(i);
}
// reverse array of chars
for (int j = 0; j < len; j++) {
charArray[j] = tempCharArray[len - 1 - j];
}
String reversePalindrome = new String(charArray);
System.out.println(reversePalindrome);
}
}
길이를 넣고 루프를 int len
사용하십시오 for
.
StringTokenizer는 문자열을 개별 문자로 나누는 작업에 전혀 적합하지 않습니다. 다음과 String#split()
같이 아무것도 일치하지 않는 정규식을 사용하여 쉽게 할 수 있습니다.
String[] theChars = str.split("|");
그러나 StringTokenizer는 정규 표현식을 사용하지 않으며 문자 사이에 아무것도 일치하지 않도록 지정할 수있는 구분자 문자열이 없습니다. 가 이다 는 구분 기호를 반환 구분 기호 문자열로 문자열 자체를 사용합니다 (모든 문자 구분을)하고있다 : 하나의 귀여운 당신이 같은 일을 수행하는 데 사용할 수있는 해킹 :
StringTokenizer st = new StringTokenizer(str, str, true);
그러나 나는 이러한 옵션을 무시할 목적으로 만 언급했습니다. 두 기술 모두 원래 문자열을 문자 기본 요소 대신 하나의 문자 문자열로 나누고 두 가지 모두 객체 생성 및 문자열 조작 형태로 많은 오버 헤드를 발생시킵니다. for 루프에서 charAt ()를 호출하는 것과 비교하면 거의 오버 헤드가 발생하지 않습니다.
위의 답변은 코드 포인트 값으로 반복하지 않는 많은 솔루션의 문제를 지적합니다 . 대리 문자 에는 문제가 있습니다 . Java 문서도 여기 에 문제를 설명합니다 ( "유니 코드 문자 표현"참조). 어쨌든 보충 유니 코드 세트의 실제 대리 문자를 사용하고 다시 문자열 로 변환하는 코드가 있습니다. .toChars ()는 문자 배열을 반환합니다. 대리자를 처리하는 경우 반드시 두 문자가 있어야합니다. 이 코드는 모든 유니 코드 문자에서 작동 합니다.
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
이 예제 코드는 당신을 도울 것입니다!
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Solution {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 10);
map.put("b", 30);
map.put("c", 50);
map.put("d", 40);
map.put("e", 20);
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
}
public static Map sortByValue(Map unsortedMap) {
Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}
class ValueComparator implements Comparator {
Map map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Object keyA, Object keyB) {
Comparable valueA = (Comparable) map.get(keyA);
Comparable valueB = (Comparable) map.get(keyB);
return valueB.compareTo(valueA);
}
}
따라서 일반적 으로이 스레드에서 여러 사람들이 이미 대답 한 Java의 문자열을 반복하는 두 가지 방법이 있습니다.
String s = sc.next() // assuming scanner class is defined above
for(int i=0; i<s.length; i++){
s.charAt(i) // This being the first way and is a constant time operation will hardly add any overhead
}
char[] str = new char[10];
str = s.toCharArray() // this is another way of doing so and it takes O(n) amount of time for copying contents from your string class to character array
성능이 위험에 처한 경우 첫 번째 일정을 일정한 시간에 사용하는 것이 좋습니다. 그렇지 않은 경우 두 번째 작업을 수행하면 Java의 문자열 클래스에 대한 불변성을 고려하여 작업을 쉽게 수행 할 수 있습니다.