문자열의 모든 문자를 반복하는 가장 빠른 방법
Java에서는 String 내의 모든 문자를 반복하는 가장 빠른 방법은 무엇입니까?
String str = "a really, really long string";
for (int i = 0, n = str.length(); i < n; i++) {
char c = str.charAt(i);
}
또는 다음과 같습니다.
char[] chars = str.toCharArray();
for (int i = 0, n = chars.length; i < n; i++) {
char c = chars[i];
}
편집:
은, 할 때의 입니다.charAt
중더 커집니다.toCharArray
첫 번째에서 어레이에 직접 액세스할 수 있습니다.
J에 대한 두 시간,하여 문자열할 수 IT의 웜업 시간, JVM의 기동 시간 등을 고려하여 스트링 길이가 다른 견고한 벤치마크를 제공할 수 있으면 좋겠다고 생각합니다.또, 다음의 2개의 콜의 차이뿐만 아니라,System.currentTimeMillis()
.
첫 번째 업데이트: 프로덕션 환경에서 이 기능을 시도하기 전에(권장하지 않음) 먼저 읽어보십시오.http://www.javaspecialists.eu/archive/Issue237.html Java 9부터는 기본적으로 Java가 문자열을 바이트로 저장하기 때문에 설명된 솔루션이 더 이상 작동하지 않습니다.
2차 업데이트: 2016년 10월 25일 현재 AMDx64 8core 및 소스 1.8에서는 'charAt'를 사용하는 것과 필드 액세스에 차이가 없습니다.jvm은 모든 string.charAt(n) 콜을 인라인 및 합리화하기 위해 충분히 최적화되어 있는 것으로 보입니다.
3차 업데이트: 2020-09-07 현재 Ryzen 1950-X 16 코어 및 소스 1.14에서 'charAt1'은 필드 액세스보다 9배 느리고 'charAt2'는 필드 액세스보다 4배 느립니다.현장접근은 확실한 승자로 돌아옵니다.Java 9+ 버전 jvms의 경우 프로그램이 바이트 []액세스를 사용해야 합니다.
.String
검사 중입니다.질문에서 알 수 있듯이 문자열이 긴 경우 문자열을 검사하는 가장 빠른 방법은 리플렉션을 사용하여 백업에 액세스하는 것입니다.char[]
을 사용하다
4 모드 8 및 한 완전 에서는 9가지 참조)이 AMD Phenom II 4 @ 955 @ 3.2 GHz(이하 참조)를 사용하고 것을 알 수 .String.charAt(n)
이 가장 .reflection
큰 문자열의 경우 String backing 어레이에 액세스하는 속도가 거의 2배 빨라집니다.
실험
9가지 최적화 기술이 시도됩니다.
모든 문자열 내용이 랜덤화됨
테스트는 0,1,2,4,8,16 등으로 시작하는 문자열 크기에 대해 2의 배수로 수행됩니다.
테스트는 문자열 크기별로 1,000회 실행됩니다.
테스트는 매번 무작위 순서로 진행됩니다.즉, 테스트가 완료될 때마다 1000회 이상 랜덤 순서로 수행됩니다.
JVM 워밍업이 최적화 및 시간에 미치는 영향을 보여주기 위해 전체 테스트 스위트가 앞으로 또는 뒤로 수행됩니다.
, 1회, 1회, 1회, 1회.
-client
와 다른-server
결론들
- 클라이언트 모드(32비트)
문자열 길이가 1~256자일 경우 호출string.charAt(i)
평균 1,340만~5억 8,800만 문자/초의 처리로 승리합니다.
또한 다음과 같이 전체적으로 5.5%,(클라이언트), 13.9%(서버)가 고속입니다.
for (int i = 0; i < data.length(); i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
로컬 최종 길이 변수에서는 다음과 같습니다.
final int len = data.length();
for (int i = 0; i < len; i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
512~256K자 길이의 긴 문자열의 경우 리플렉션을 사용하여 String의 백업 배열에 액세스하는 것이 가장 빠릅니다.이 기술은 String.charAt(i)의 거의 2배(178% 고속)입니다.이 범위의 평균 속도는 초당 11억1100만 문자였습니다.
필드는 미리 가져와야 하며 라이브러리에서 다른 문자열로 재사용할 수 있습니다.흥미롭게도 위의 코드와 달리 필드 액세스에서는 로컬 최종 길이 변수를 갖는 것이 루프 체크에서 'chars.length'를 사용하는 것보다 9% 더 빠릅니다.필드 액세스를 가장 빠르게 설정하는 방법은 다음과 같습니다.
final Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
try {
final char[] chars = (char[]) field.get(data);
final int len = chars.length;
for (int i = 0; i < len; i++) {
if (chars[i] <= ' ') {
doThrow();
}
}
return len;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
-server 모드에 대한 특별 설명
AMD 64 머신의 64비트 Java 머신의 서버 모드에서 32글자 길이의 문자열 후에 필드 액세스가 시작됩니다.클라이언트 모드에서는 512자까지 표시되지 않았습니다.
또, 서버 모드로 JDK 8(32비트 빌드)을 실행하고 있을 때는, 큰 문자열과 작은 문자열의 양쪽 모두에서, 전체적인 퍼포먼스가 7% 늦었습니다.이것은 JDK 8 조기 릴리스의 빌드 121 2013년 12월입니다.따라서 현시점에서는 32비트서버 모드가 32비트클라이언트 모드보다 느린 것 같습니다.
말하자면...호출할 가치가 있는 서버 모드는 64비트 머신뿐인 것 같습니다.그렇지 않으면 성능이 저하됩니다.
를 32비트로 -server mode
AMD64 amd amd amd amd amd amd amd amd amd amd amd amd amd amd 。
- String.charAt(i)는 전체적으로 확실한 승자입니다.크기는 8자에서 512자 사이이지만 '새로운' '재사용'과 '필드' 중 우승자가 있었다.
- String.charAt(i)는 클라이언트모드로 45% 고속화
- 클라이언트 모드의 큰 문자열의 필드 액세스 속도는 2배입니다.
String.chars() (Stream " " " " " ) 。을 사용하다Streams
API는 일반적인 문자열 작업을 수행하는 데 다소 느린 방법입니다.
위시리스트
Java String contains 、 Each ( consumer ) 、 ForWithIndex ( consumer ) 。메서드에 콜의 을 알 이러한 은 라이브러리 .beep-beep beep
도를높높 높높높다다
계속 꿈꾸다 :)
해피 스트링스!
~SH
이 테스트에서는 다음 9가지 방법으로 문자열을 테스트하여 공백이 존재하는지 여부를 테스트했습니다.
"Charat1" -- 일반적인 방법으로 문자열 내용을 확인합니다.
int charAtMethod1(final String data) {
final int len = data.length();
for (int i = 0; i < len; i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
return len;
}
"Charat2" -- 위와 같지만 LENGTh의 최종 로컬 int 대신 String.length()를 사용합니다.
int charAtMethod2(final String data) {
for (int i = 0; i < data.length(); i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
return data.length();
}
"stream" -- 새로운 Java-8 String의 IntStream을 사용하여 확인을 위해 술어를 전달합니다.
int streamMethod(final String data, final IntPredicate predicate) {
if (data.chars().anyMatch(predicate)) {
doThrow();
}
return data.length();
}
"StreamPara" - 위와 같으나 OH-LA-LA-Parallel!!
// avoid this at all costs
int streamParallelMethod(final String data, IntPredicate predicate) {
if (data.chars().parallel().anyMatch(predicate)) {
doThrow();
}
return data.length();
}
"reuse" -- 재사용 가능한 문자를 문자열 내용으로 다시 채웁니다.
int reuseBuffMethod(final char[] reusable, final String data) {
final int len = data.length();
data.getChars(0, len, reusable, 0);
for (int i = 0; i < len; i++) {
if (reusable[i] <= ' ') {
doThrow();
}
}
return len;
}
"new1" -- 문자열에서 새 문자 복사본을 가져옵니다.
int newMethod1(final String data) {
final int len = data.length();
final char[] copy = data.toCharArray();
for (int i = 0; i < len; i++) {
if (copy[i] <= ' ') {
doThrow();
}
}
return len;
}
"new2" -- 위와 같으나 "각자"를 사용합니다.
int newMethod2(final String data) {
for (final char c : data.toCharArray()) {
if (c <= ' ') {
doThrow();
}
}
return data.length();
}
"field1" - FANCY!!문자열 내부 문자 []에 액세스하기 위한 필드 가져오기
int fieldMethod1(final Field field, final String data) {
try {
final char[] chars = (char[]) field.get(data);
final int len = chars.length;
for (int i = 0; i < len; i++) {
if (chars[i] <= ' ') {
doThrow();
}
}
return len;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
"field2" - 위와 동일하지만 "for-each"를 사용합니다.
int fieldMethod2(final Field field, final String data) {
final char[] chars;
try {
chars = (char[]) field.get(data);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
for (final char c : chars) {
if (c <= ' ') {
doThrow();
}
}
return chars.length;
}
Client-client
MODE(모드)
주의: AMD64 머신의 Java 32비트의 -client 모드와 Java 64비트의 -server 모드는 다음과 같습니다.
Size WINNER charAt1 charAt2 stream streamPar reuse new1 new2 field1 field2
1 charAt 77.0 72.0 462.0 584.0 127.5 89.5 86.0 159.5 165.0
2 charAt 38.0 36.5 284.0 32712.5 57.5 48.3 50.3 89.0 91.5
4 charAt 19.5 18.5 458.6 3169.0 33.0 26.8 27.5 54.1 52.6
8 charAt 9.8 9.9 100.5 1370.9 17.3 14.4 15.0 26.9 26.4
16 charAt 6.1 6.5 73.4 857.0 8.4 8.2 8.3 13.6 13.5
32 charAt 3.9 3.7 54.8 428.9 5.0 4.9 4.7 7.0 7.2
64 charAt 2.7 2.6 48.2 232.9 3.0 3.2 3.3 3.9 4.0
128 charAt 2.1 1.9 43.7 138.8 2.1 2.6 2.6 2.4 2.6
256 charAt 1.9 1.6 42.4 90.6 1.7 2.1 2.1 1.7 1.8
512 field1 1.7 1.4 40.6 60.5 1.4 1.9 1.9 1.3 1.4
1,024 field1 1.6 1.4 40.0 45.6 1.2 1.9 2.1 1.0 1.2
2,048 field1 1.6 1.3 40.0 36.2 1.2 1.8 1.7 0.9 1.1
4,096 field1 1.6 1.3 39.7 32.6 1.2 1.8 1.7 0.9 1.0
8,192 field1 1.6 1.3 39.6 30.5 1.2 1.8 1.7 0.9 1.0
16,384 field1 1.6 1.3 39.8 28.4 1.2 1.8 1.7 0.8 1.0
32,768 field1 1.6 1.3 40.0 26.7 1.3 1.8 1.7 0.8 1.0
65,536 field1 1.6 1.3 39.8 26.3 1.3 1.8 1.7 0.8 1.0
131,072 field1 1.6 1.3 40.1 25.4 1.4 1.9 1.8 0.8 1.0
262,144 field1 1.6 1.3 39.6 25.2 1.5 1.9 1.9 0.8 1.0
SERVER의 SERVER의 -server
MODE(모드)
주의: 이 테스트는 AMD64에서 서버 모드로 실행되는 Java 32비트 테스트입니다.Java 64비트의 서버 모드는 클라이언트모드의 Java 32비트와 동일하지만 필드접근은 32자 크기 후에 시작됩니다.
Size WINNER charAt1 charAt2 stream streamPar reuse new1 new2 field1 field2
1 charAt 74.5 95.5 524.5 783.0 90.5 102.5 90.5 135.0 151.5
2 charAt 48.5 53.0 305.0 30851.3 59.3 57.5 52.0 88.5 91.8
4 charAt 28.8 32.1 132.8 2465.1 37.6 33.9 32.3 49.0 47.0
8 new2 18.0 18.6 63.4 1541.3 18.5 17.9 17.6 25.4 25.8
16 new2 14.0 14.7 129.4 1034.7 12.5 16.2 12.0 16.0 16.6
32 new2 7.8 9.1 19.3 431.5 8.1 7.0 6.7 7.9 8.7
64 reuse 6.1 7.5 11.7 204.7 3.5 3.9 4.3 4.2 4.1
128 reuse 6.8 6.8 9.0 101.0 2.6 3.0 3.0 2.6 2.7
256 field2 6.2 6.5 6.9 57.2 2.4 2.7 2.9 2.3 2.3
512 reuse 4.3 4.9 5.8 28.2 2.0 2.6 2.6 2.1 2.1
1,024 charAt 2.0 1.8 5.3 17.6 2.1 2.5 3.5 2.0 2.0
2,048 charAt 1.9 1.7 5.2 11.9 2.2 3.0 2.6 2.0 2.0
4,096 charAt 1.9 1.7 5.1 8.7 2.1 2.6 2.6 1.9 1.9
8,192 charAt 1.9 1.7 5.1 7.6 2.2 2.5 2.6 1.9 1.9
16,384 charAt 1.9 1.7 5.1 6.9 2.2 2.5 2.5 1.9 1.9
32,768 charAt 1.9 1.7 5.1 6.1 2.2 2.5 2.5 1.9 1.9
65,536 charAt 1.9 1.7 5.1 5.5 2.2 2.4 2.4 1.9 1.9
131,072 charAt 1.9 1.7 5.1 5.4 2.3 2.5 2.5 1.9 1.9
262,144 charAt 1.9 1.7 5.1 5.1 2.3 2.5 2.5 1.9 1.9
풀 실행 가능한 프로그램 코드
(Java 7 이전 버전에서 테스트하려면 2개의 스트림테스트를 삭제합니다)
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.IntPredicate;
/**
* @author Saint Hill <http://stackoverflow.com/users/1584255/saint-hill>
*/
public final class TestStrings {
// we will not test strings longer than 512KM
final int MAX_STRING_SIZE = 1024 * 256;
// for each string size, we will do all the tests
// this many times
final int TRIES_PER_STRING_SIZE = 1000;
public static void main(String[] args) throws Exception {
new TestStrings().run();
}
void run() throws Exception {
// double the length of the data until it reaches MAX chars long
// 0,1,2,4,8,16,32,64,128,256 ...
final List<Integer> sizes = new ArrayList<>();
for (int n = 0; n <= MAX_STRING_SIZE; n = (n == 0 ? 1 : n * 2)) {
sizes.add(n);
}
// CREATE RANDOM (FOR SHUFFLING ORDER OF TESTS)
final Random random = new Random();
System.out.println("Rate in nanoseconds per character inspected.");
System.out.printf("==== FORWARDS (tries per size: %s) ==== \n", TRIES_PER_STRING_SIZE);
printHeadings(TRIES_PER_STRING_SIZE, random);
for (int size : sizes) {
reportResults(size, test(size, TRIES_PER_STRING_SIZE, random));
}
// reverse order or string sizes
Collections.reverse(sizes);
System.out.println("");
System.out.println("Rate in nanoseconds per character inspected.");
System.out.printf("==== BACKWARDS (tries per size: %s) ==== \n", TRIES_PER_STRING_SIZE);
printHeadings(TRIES_PER_STRING_SIZE, random);
for (int size : sizes) {
reportResults(size, test(size, TRIES_PER_STRING_SIZE, random));
}
}
///
///
/// METHODS OF CHECKING THE CONTENTS
/// OF A STRING. ALWAYS CHECKING FOR
/// WHITESPACE (CHAR <=' ')
///
///
// CHECK THE STRING CONTENTS
int charAtMethod1(final String data) {
final int len = data.length();
for (int i = 0; i < len; i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
return len;
}
// SAME AS ABOVE BUT USE String.length()
// instead of making a new final local int
int charAtMethod2(final String data) {
for (int i = 0; i < data.length(); i++) {
if (data.charAt(i) <= ' ') {
doThrow();
}
}
return data.length();
}
// USE new Java-8 String's IntStream
// pass it a PREDICATE to do the checking
int streamMethod(final String data, final IntPredicate predicate) {
if (data.chars().anyMatch(predicate)) {
doThrow();
}
return data.length();
}
// OH LA LA - GO PARALLEL!!!
int streamParallelMethod(final String data, IntPredicate predicate) {
if (data.chars().parallel().anyMatch(predicate)) {
doThrow();
}
return data.length();
}
// Re-fill a resuable char[] with the contents
// of the String's char[]
int reuseBuffMethod(final char[] reusable, final String data) {
final int len = data.length();
data.getChars(0, len, reusable, 0);
for (int i = 0; i < len; i++) {
if (reusable[i] <= ' ') {
doThrow();
}
}
return len;
}
// Obtain a new copy of char[] from String
int newMethod1(final String data) {
final int len = data.length();
final char[] copy = data.toCharArray();
for (int i = 0; i < len; i++) {
if (copy[i] <= ' ') {
doThrow();
}
}
return len;
}
// Obtain a new copy of char[] from String
// but use FOR-EACH
int newMethod2(final String data) {
for (final char c : data.toCharArray()) {
if (c <= ' ') {
doThrow();
}
}
return data.length();
}
// FANCY!
// OBTAIN FIELD FOR ACCESS TO THE STRING'S
// INTERNAL CHAR[]
int fieldMethod1(final Field field, final String data) {
try {
final char[] chars = (char[]) field.get(data);
final int len = chars.length;
for (int i = 0; i < len; i++) {
if (chars[i] <= ' ') {
doThrow();
}
}
return len;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
// same as above but use FOR-EACH
int fieldMethod2(final Field field, final String data) {
final char[] chars;
try {
chars = (char[]) field.get(data);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
for (final char c : chars) {
if (c <= ' ') {
doThrow();
}
}
return chars.length;
}
/**
*
* Make a list of tests. We will shuffle a copy of this list repeatedly
* while we repeat this test.
*
* @param data
* @return
*/
List<Jobber> makeTests(String data) throws Exception {
// make a list of tests
final List<Jobber> tests = new ArrayList<Jobber>();
tests.add(new Jobber("charAt1") {
int check() {
return charAtMethod1(data);
}
});
tests.add(new Jobber("charAt2") {
int check() {
return charAtMethod2(data);
}
});
tests.add(new Jobber("stream") {
final IntPredicate predicate = new IntPredicate() {
public boolean test(int value) {
return value <= ' ';
}
};
int check() {
return streamMethod(data, predicate);
}
});
tests.add(new Jobber("streamPar") {
final IntPredicate predicate = new IntPredicate() {
public boolean test(int value) {
return value <= ' ';
}
};
int check() {
return streamParallelMethod(data, predicate);
}
});
// Reusable char[] method
tests.add(new Jobber("reuse") {
final char[] cbuff = new char[MAX_STRING_SIZE];
int check() {
return reuseBuffMethod(cbuff, data);
}
});
// New char[] from String
tests.add(new Jobber("new1") {
int check() {
return newMethod1(data);
}
});
// New char[] from String
tests.add(new Jobber("new2") {
int check() {
return newMethod2(data);
}
});
// Use reflection for field access
tests.add(new Jobber("field1") {
final Field field;
{
field = String.class.getDeclaredField("value");
field.setAccessible(true);
}
int check() {
return fieldMethod1(field, data);
}
});
// Use reflection for field access
tests.add(new Jobber("field2") {
final Field field;
{
field = String.class.getDeclaredField("value");
field.setAccessible(true);
}
int check() {
return fieldMethod2(field, data);
}
});
return tests;
}
/**
* We use this class to keep track of test results
*/
abstract class Jobber {
final String name;
long nanos;
long chars;
long runs;
Jobber(String name) {
this.name = name;
}
abstract int check();
final double nanosPerChar() {
double charsPerRun = chars / runs;
long nanosPerRun = nanos / runs;
return charsPerRun == 0 ? nanosPerRun : nanosPerRun / charsPerRun;
}
final void run() {
runs++;
long time = System.nanoTime();
chars += check();
nanos += System.nanoTime() - time;
}
}
// MAKE A TEST STRING OF RANDOM CHARACTERS A-Z
private String makeTestString(int testSize, char start, char end) {
Random r = new Random();
char[] data = new char[testSize];
for (int i = 0; i < data.length; i++) {
data[i] = (char) (start + r.nextInt(end));
}
return new String(data);
}
// WE DO THIS IF WE FIND AN ILLEGAL CHARACTER IN THE STRING
public void doThrow() {
throw new RuntimeException("Bzzzt -- Illegal Character!!");
}
/**
* 1. get random string of correct length 2. get tests (List<Jobber>) 3.
* perform tests repeatedly, shuffling each time
*/
List<Jobber> test(int size, int tries, Random random) throws Exception {
String data = makeTestString(size, 'A', 'Z');
List<Jobber> tests = makeTests(data);
List<Jobber> copy = new ArrayList<>(tests);
while (tries-- > 0) {
Collections.shuffle(copy, random);
for (Jobber ti : copy) {
ti.run();
}
}
// check to make sure all char counts the same
long runs = tests.get(0).runs;
long count = tests.get(0).chars;
for (Jobber ti : tests) {
if (ti.runs != runs && ti.chars != count) {
throw new Exception("Char counts should match if all correct algorithms");
}
}
return tests;
}
private void printHeadings(final int TRIES_PER_STRING_SIZE, final Random random) throws Exception {
System.out.print(" Size");
for (Jobber ti : test(0, TRIES_PER_STRING_SIZE, random)) {
System.out.printf("%9s", ti.name);
}
System.out.println("");
}
private void reportResults(int size, List<Jobber> tests) {
System.out.printf("%6d", size);
for (Jobber ti : tests) {
System.out.printf("%,9.2f", ti.nanosPerChar());
}
System.out.println("");
}
}
이것은 단지 당신이 걱정할 필요가 없는 미세 최적화입니다.
char[] chars = str.toCharArray();
의 복사본을 반환한다.str
배열에서는, 「」(JDK)를 호출해 합니다).System.arrayCopy
를 참조해 주세요.
외는 ★★★★★★★★★★★★★★★★★★.str.charAt()
는 인덱스가 실제로 경계 내에 있는지 확인하고 배열 인덱스 내의 문자를 반환합니다.
첫 번째는 JVM에 추가 메모리를 생성하지 않습니다.
단지 호기심 때문에 그리고 세인트 힐의 대답과 비교하기 위해서.
대량의 데이터를 처리해야 하는 경우 클라이언트 모드에서 JVM을 사용하지 마십시오.최적화를 위해 클라이언트 모드가 만들어지지 않았습니다.
클라이언트 모드와 서버 모드에서 JVM을 사용하여 @Saint Hill 벤치마크 결과를 비교합니다.
Core2Quad Q6600 G0 @ 2.4GHz
JavaSE 1.7.0_40
참고 항목: "java - server"와 "java - client"의 실제 차이점은 무엇입니까?
클라이언트 모드:
len = 2: 111k charAt(i), 105k cbuff[i], 62k new[i], 17k field access. (chars/ms)
len = 4: 285k charAt(i), 166k cbuff[i], 114k new[i], 43k field access. (chars/ms)
len = 6: 315k charAt(i), 230k cbuff[i], 162k new[i], 69k field access. (chars/ms)
len = 8: 333k charAt(i), 275k cbuff[i], 181k new[i], 85k field access. (chars/ms)
len = 12: 342k charAt(i), 342k cbuff[i], 222k new[i], 117k field access. (chars/ms)
len = 16: 363k charAt(i), 347k cbuff[i], 275k new[i], 152k field access. (chars/ms)
len = 20: 363k charAt(i), 392k cbuff[i], 289k new[i], 180k field access. (chars/ms)
len = 24: 375k charAt(i), 428k cbuff[i], 311k new[i], 205k field access. (chars/ms)
len = 28: 378k charAt(i), 474k cbuff[i], 341k new[i], 233k field access. (chars/ms)
len = 32: 376k charAt(i), 492k cbuff[i], 340k new[i], 251k field access. (chars/ms)
len = 64: 374k charAt(i), 551k cbuff[i], 374k new[i], 367k field access. (chars/ms)
len = 128: 385k charAt(i), 624k cbuff[i], 415k new[i], 509k field access. (chars/ms)
len = 256: 390k charAt(i), 675k cbuff[i], 436k new[i], 619k field access. (chars/ms)
len = 512: 394k charAt(i), 703k cbuff[i], 439k new[i], 695k field access. (chars/ms)
len = 1024: 395k charAt(i), 718k cbuff[i], 462k new[i], 742k field access. (chars/ms)
len = 2048: 396k charAt(i), 725k cbuff[i], 471k new[i], 767k field access. (chars/ms)
len = 4096: 396k charAt(i), 727k cbuff[i], 459k new[i], 780k field access. (chars/ms)
len = 8192: 397k charAt(i), 712k cbuff[i], 446k new[i], 772k field access. (chars/ms)
서버 모드:
len = 2: 86k charAt(i), 41k cbuff[i], 46k new[i], 80k field access. (chars/ms)
len = 4: 571k charAt(i), 250k cbuff[i], 97k new[i], 222k field access. (chars/ms)
len = 6: 666k charAt(i), 333k cbuff[i], 125k new[i], 315k field access. (chars/ms)
len = 8: 800k charAt(i), 400k cbuff[i], 181k new[i], 380k field access. (chars/ms)
len = 12: 800k charAt(i), 521k cbuff[i], 260k new[i], 545k field access. (chars/ms)
len = 16: 800k charAt(i), 592k cbuff[i], 296k new[i], 640k field access. (chars/ms)
len = 20: 800k charAt(i), 666k cbuff[i], 408k new[i], 800k field access. (chars/ms)
len = 24: 800k charAt(i), 705k cbuff[i], 452k new[i], 800k field access. (chars/ms)
len = 28: 777k charAt(i), 736k cbuff[i], 368k new[i], 933k field access. (chars/ms)
len = 32: 800k charAt(i), 780k cbuff[i], 571k new[i], 969k field access. (chars/ms)
len = 64: 800k charAt(i), 901k cbuff[i], 800k new[i], 1306k field access. (chars/ms)
len = 128: 1084k charAt(i), 888k cbuff[i], 633k new[i], 1620k field access. (chars/ms)
len = 256: 1122k charAt(i), 966k cbuff[i], 729k new[i], 1790k field access. (chars/ms)
len = 512: 1163k charAt(i), 1007k cbuff[i], 676k new[i], 1910k field access. (chars/ms)
len = 1024: 1179k charAt(i), 1027k cbuff[i], 698k new[i], 1954k field access. (chars/ms)
len = 2048: 1184k charAt(i), 1043k cbuff[i], 732k new[i], 2007k field access. (chars/ms)
len = 4096: 1188k charAt(i), 1049k cbuff[i], 742k new[i], 2031k field access. (chars/ms)
len = 8192: 1157k charAt(i), 1032k cbuff[i], 723k new[i], 2048k field access. (chars/ms)
결론:
보시다시피 서버 모드가 훨씬 빠릅니다.
사용법.'를 사용한 입니다.str.charAt
더 빨라야 합니다.
「 」의 , 「 ★★★★★★★★★★★」String
에서는 '수업 '수업', '수업', '수업'을 볼 수 .charAt
같이 구현됩니다.
public char charAt(int index) {
if ((index < 0) || (index >= count)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index + offset];
}
여기서는 어레이를 인덱스하고 값을 반환하기만 하면 됩니다.
''의 , ''의 실장'을 보면,toCharArray
이하를 참조해 주세요.
public char[] toCharArray() {
char result[] = new char[count];
getChars(0, count, result, 0);
return result;
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > count) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, offset + srcBegin, dst, dstBegin,
srcEnd - srcBegin);
}
이것은 , 하다, 하다를 있다.System.arraycopy
안하하 것것것 거거거 거거거
String.toCharArray()
char 배열을 문자열 길이의 "char"를 사용하여 문자열의 합니다.이것은 문자열 길이의 메모리 할당을 의미합니다.그 후 다음 명령을 사용하여 문자열의 원래 char 배열을 복사합니다.System.arraycopy()
아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 맞다.String는 String.charAt() 위치에 합니다.i
원본에서, 그래서String.charAt()
빠르다String.toCharArray()
. 록일 ~ 일일일 。String.toCharArray()
는 원래 서 "String" "char" "copy" 는 "String" "car" 입니다.String.charAt()
이치노아래 코드는 이 문자열의 지정된 인덱스에서 값을 반환합니다.
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
아래 코드는 이 문자열의 길이인 새로 할당된 문자 배열을 반환합니다.
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
str.toCharArray()의 시간 복잡성을 고려하면 @Saint Hill의 답변에도 불구하고
첫 번째는 아주 큰 현이라도 더 빠릅니다.아래 코드를 실행하여 직접 확인할 수 있습니다.
char [] ch = new char[1_000_000_00];
String str = new String(ch); // to create a large string
// ---> from here
long currentTime = System.nanoTime();
for (int i = 0, n = str.length(); i < n; i++) {
char c = str.charAt(i);
}
// ---> to here
System.out.println("str.charAt(i):"+(System.nanoTime()-currentTime)/1000000.0 +" (ms)");
/**
* ch = str.toCharArray() itself takes lots of time
*/
// ---> from here
currentTime = System.nanoTime();
ch = str.toCharArray();
for (int i = 0, n = str.length(); i < n; i++) {
char c = ch[i];
}
// ---> to here
System.out.println("ch = str.toCharArray() + c = ch[i] :"+(System.nanoTime()-currentTime)/1000000.0 +" (ms)");
출력:
str.charAt(i):5.492102 (ms)
ch = str.toCharArray() + c = ch[i] :79.400064 (ms)
니더가 더 빠르거나 더 느린 것 같아
public static void main(String arguments[]) {
//Build a long string
StringBuilder sb = new StringBuilder();
for(int j = 0; j < 10000; j++) {
sb.append("a really, really long string");
}
String str = sb.toString();
for (int testscount = 0; testscount < 10; testscount ++) {
//Test 1
long start = System.currentTimeMillis();
for(int c = 0; c < 10000000; c++) {
for (int i = 0, n = str.length(); i < n; i++) {
char chr = str.charAt(i);
doSomethingWithChar(chr);//To trick JIT optimistaion
}
}
System.out.println("1: " + (System.currentTimeMillis() - start));
//Test 2
start = System.currentTimeMillis();
char[] chars = str.toCharArray();
for(int c = 0; c < 10000000; c++) {
for (int i = 0, n = chars.length; i < n; i++) {
char chr = chars[i];
doSomethingWithChar(chr);//To trick JIT optimistaion
}
}
System.out.println("2: " + (System.currentTimeMillis() - start));
System.out.println();
}
}
public static void doSomethingWithChar(char chr) {
int newInt = chr << 2;
}
긴 끈은 첫 번째 끈으로 하겠습니다.왜 긴 줄을 따라다니죠?문서에는 다음과 같이 기재되어 있습니다.
public char[] to CharArray() 이 문자열을 새 문자 배열로 변환합니다.
반환: 길이가 이 문자열의 길이이고 내용이 이 문자열로 표현되는 문자 시퀀스를 포함하도록 초기화되는 새로 할당된 문자 배열입니다.
//편집 1
JIT 최적화를 속이기 위해 테스트를 변경했습니다.
//편집 2
테스트를 10회 반복하여 JVM을 예열합니다.
//편집 3
결론:
★★★★★★★★★★★★★★★.str.toCharArray();
전체를 memorymemory에 합니다.긴 문자열에서는 메모리가 많이 소모될 수 있습니다. ★★String.charAt( )
스트링 문자 문자】【문자】【문자】【문자】【문자】【문자】Strings first 메서드는 충분히 짧은 것 같습니다(즉,chatAt
method는 이 때문에 .그러나 문자열이 충분히 길면 문자 배열 전체를 복사하는 속도가 느려지고 첫 번째 방법이 더 빠릅니다..toCharArray
여러분도 한 번 .for(int j = 0; j < 10000; j++)
루프하여 확인합니다.JVM 웜업 코드를 실행하면 실행 속도는 빨라지지만 비율은 동일합니다.
결국 그것은 단지 미세 최적화일 뿐이다.
두 번째는 새로운 char 배열이 생성되고 String의 모든 char가 이 새로운 char 배열로 복사되므로 첫 번째 char 배열이 더 빠르고 메모리 사용량이 적을 것입니다.
언급URL : https://stackoverflow.com/questions/8894258/fastest-way-to-iterate-over-all-the-chars-in-a-string
'itsource' 카테고리의 다른 글
"short 30 = 3 * 10"이 법적 할당인 이유는 무엇입니까? (0) | 2022.09.26 |
---|---|
INSERT IN을 개선하는 방법...잠금 동작 선택 (0) | 2022.09.26 |
다차원 배열의 모든 하위 배열 요소를 다시 인덱싱하려면 어떻게 해야 합니까? (0) | 2022.09.26 |
사전의 최소값에 해당하는 키를 가져옵니다. (0) | 2022.09.26 |
Javascript : 자연스러운 영숫자 문자열 (0) | 2022.09.25 |