В Java, какой бы самый быстрый способ перебрать все символы в строке, это:
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
в начале и последующего прямого доступа к массиву во время итерации.
Было бы замечательно, если бы кто-то мог обеспечить надежный тест для разных длин строк, имея в виду время прогрева JIT, время запуска JVM и т. Д., А не только разницу между двумя вызовами System.currentTimeMillis()
.
for (char c : chars)
?charAt
меньше или больше стоимости выполнения одного звонкаtoCharArray
Ответы:
ПЕРВОЕ ОБНОВЛЕНИЕ: прежде чем пытаться делать это когда-либо в производственной среде (не рекомендуется), сначала прочтите это: http://www.javaspecialists.eu/archive/Issue237.html Начиная с Java 9, описанное решение больше не будет работать потому что теперь Java будет хранить строки как byte [] по умолчанию.
ВТОРОЕ ОБНОВЛЕНИЕ: По состоянию на 2016-10-25, на моем 8xore AMDx64 и источнике 1.8 нет никакой разницы между использованием 'charAt' и доступом к полю. Похоже, что jvm достаточно оптимизирован, чтобы встроить и оптимизировать любые вызовы 'string.charAt (n)'.
Все зависит от длины
String
проверяемого. Если, как говорится в вопросе, это для длинных строк, самый быстрый способ проверить строку - это использовать отражение для доступа к поддержкеchar[]
строки.Полностью рандомизированный эталонный тест с JDK 8 (win32 и win64) на 64-ядерном ядре Phenom II 4 AMD 955 @ 3,2 ГГц (как в режиме клиента, так и в режиме сервера) с 9 различными методами (см. Ниже!) Показывает, что использование
String.charAt(n)
является самым быстрым для малых строк и что использованиеreflection
для доступа к массиву поддержки строк почти вдвое быстрее для больших строк.ЭКСПЕРИМЕНТ
Испробованы 9 различных методов оптимизации.
Все содержимое строки рандомизировано
Тест проводится для размеров строк, кратных двум, начиная с 0,1,2,4,8,16 и т. Д.
Тесты проводятся 1000 раз на размер строки
Тесты перемешиваются в случайном порядке каждый раз. Другими словами, тесты проводятся в случайном порядке каждый раз, более 1000 раз.
Весь набор тестов выполняется вперед и назад, чтобы показать влияние прогрева JVM на оптимизацию и время.
Весь набор выполняется дважды, один в
-client
режиме, а другой в-server
режиме.ВЫВОДЫ
-клиентский режим (32 бита)
Для строк длиной от 1 до 256 символов
string.charAt(i)
выигрывает вызов со средней обработкой от 13,4 до 588 миллионов символов в секунду.Кроме того, это в целом на 5,5% быстрее (клиент) и 13,9% (сервер), например:
чем это с локальной конечной переменной длины:
Для длинных строк длиной от 512 до 256K символов наиболее быстрым является использование отражения для доступа к массиву поддержки String. Этот метод почти в два раза быстрее String.charAt (i) (на 178% быстрее). Средняя скорость в этом диапазоне составила 1,111 миллиарда символов в секунду.
Поле должно быть получено заранее, а затем оно может быть повторно использовано в библиотеке в разных строках. Интересно, что в отличие от кода выше, с доступом к полю на 9% быстрее иметь локальную конечную переменную длины, чем использовать 'chars.length' в проверке цикла. Вот как доступ к полю можно настроить максимально быстро:
Специальные комментарии по режиму -server
Доступ к полю начинает выигрывать после 32-х символьных строк в режиме сервера на 64-битной машине Java на моей машине AMD 64. Это не было видно до 512 символов в режиме клиента.
Также стоит отметить, что, когда я запускал JDK 8 (32-битная сборка) в режиме сервера, общая производительность была на 7% ниже как для больших, так и для маленьких строк. Это было в сборке 121 декабря 2013 года JDK 8 раннего выпуска. Итак, на данный момент кажется, что 32-битный режим сервера медленнее, чем 32-битный режим клиента.
При этом ... похоже, что единственный режим сервера, который стоит использовать, находится на 64-битной машине. В противном случае это фактически снижает производительность.
Для 32-битной сборки, работающей
-server mode
на AMD64, я могу сказать следующее:Также стоит сказать, что String.chars () (Stream и параллельная версия) - это перебор. Намного медленнее, чем любой другой способ.
Streams
API является довольно медленным способом для выполнения операций над строками вообще.Список желаний
Java String может иметь предикат, принимающий оптимизированные методы, такие как содержит (предикат), forEach (потребитель), forEachWithIndex (потребитель). Таким образом, если пользователю не нужно знать длину или повторять вызовы методов String, это может помочь в
beep-beep beep
ускорении анализа библиотек .Мечтай дальше :)
Счастливые Струны!
~ SH
В тесте использовались следующие 9 методов проверки строки на наличие пробелов:
"charAt1" - ПРОВЕРИТЬ СОДЕРЖАНИЕ СОДЕРЖАНИЕ ОБЫЧНЫМ ПУТИ:
"charAt2" - ЖЕ, КАК ВЫШЕ, НО ИСПОЛЬЗОВАТЬ
"stream" - ИСПОЛЬЗУЙТЕ НОВУЮ СТРОКУ JAVA-8 и ПРОЙДИТЕ ЭТО ПРЕДИКАТ ДЛЯ ПРОВЕРКИ
"streamPara" - ЖЕ, КАК ВЫШЕ, НО ОХ-ЛА-ЛА - ПУТЕШЕСТВУЙ!
"reuse" - ЗАПОЛНИТЬ ОБРАЗУЮЩИЙСЯ ЧАР
"new1" - ПОЛУЧИТЕ НОВУЮ КОПИЮ ЧЕРНОГО [] ИЗ СТРУНЫ
"new2" - ТАК ЖЕ ВЫШЕ, НО ИСПОЛЬЗУЙТЕ "ДЛЯ КАЖДОГО"
"field1" - FANCY !! ПОЛУЧИТЕ ПОЛЕ для доступа к внутреннему символу STRING []
"field2" - ТАК ЖЕ ВЫШЕ, НО ИСПОЛЬЗУЙТЕ "ДЛЯ КАЖДОГО"
КОМПОЗИЦИОННЫЕ РЕЗУЛЬТАТЫ ДЛЯ
-client
РЕЖИМА КЛИЕНТА (комбинированные тесты вперед и назад)Обратите внимание: режим -client с 32-битным Java и режим -server с 64-битным Java такой же, как показано ниже на моей машине с AMD64.
КОМПОЗИЦИОННЫЕ РЕЗУЛЬТАТЫ ДЛЯ
-server
РЕЖИМА СЕРВЕРА (комбинированные тесты вперед и назад)Примечание: это тест для 32-битной Java, работающей в режиме сервера на AMD64. Режим сервера для 64-битной Java был таким же, как 32-битная Java в режиме клиента, за исключением того, что доступ к полю начинался с выигрыша после 32-символьного размера.
ПОЛНЫЙ РАБОТАЮЩИЙ КОД ПРОГРАММЫ
(для тестирования на Java 7 и более ранних версиях удалите тесты двух потоков)
источник
Это просто микрооптимизация, о которой вам не стоит беспокоиться.
возвращает вам копию
str
символьных массивов (в JDK он возвращает копию символов путем вызоваSystem.arrayCopy
).Кроме этого,
str.charAt()
только проверяет, действительно ли индекс находится в границах, и возвращает символ в индексе массива.Первый не создает дополнительной памяти в JVM.
источник
Просто для любопытства и для сравнения с ответом Сент-Хилла.
Если вам нужно обрабатывать тяжелые данные, вы не должны использовать JVM в режиме клиента. Клиентский режим не предназначен для оптимизации.
Давайте сравним результаты тестов @Saint Hill с использованием JVM в режиме клиента и режиме сервера.
Смотрите также: Реальные различия между "java -server" и "java -client"?
КЛИЕНТСКИЙ РЕЖИМ:
РЕЖИМ СЕРВЕРА:
ВЫВОД:
Как видите, режим сервера намного быстрее.
источник
Первое использование
str.charAt
должно быть быстрее.Если вы покопаетесь в исходном коде
String
класса, мы увидим, чтоcharAt
это реализовано следующим образом:Здесь все, что он делает, это индексирует массив и возвращает значение.
Теперь, если мы увидим реализацию
toCharArray
, мы найдем следующее:Как вы видите, он делает,
System.arraycopy
что, безусловно, будет медленнее, чем не делает.источник
Несмотря на ответ @Saint Hill, если учесть временную сложность str.toCharArray () ,
первый быстрее даже для очень больших струн. Вы можете запустить код ниже, чтобы увидеть его для себя.
вывод:
источник
Похоже, быстрее или медленнее
Для длинных струн я выберу первый. Зачем копировать длинные строки? Документация гласит:
// Редактировать 1
Я изменил тест, чтобы обмануть JIT-оптимизацию.
// Редактировать 2
Повторите тест 10 раз, чтобы дать JVM прогреться.
// Редактировать 3
Выводы:
Прежде всего
str.toCharArray();
копирует всю строку в память. Это может занять много памяти для длинных строк. МетодString.charAt( )
ищет char в массиве char внутри String, проверяя индекс раньше. Похоже, что для достаточно коротких строк первый метод (т. Е.chatAt
Метод) немного медленнее из-за этой проверки индекса. Но если String достаточно длинный, копирование всего массива char происходит медленнее, а первый метод - быстрее. Чем длиннее строка, тем медленнееtoCharArray
работает. Попробуйте изменить предел вfor(int j = 0; j < 10000; j++)
цикле, чтобы увидеть его. Если мы дадим JVM прогреться, код будет работать быстрее, но пропорции будут такими же.Ведь это просто микрооптимизация.
источник
for:in
вариант, просто для удовольствия?Iterable
ни массивом.String.toCharArray()
создает новый массив символов, означает выделение памяти длины строки, затем копирует исходный массив символов строки с помощьюSystem.arraycopy()
и затем возвращает эту копию вызывающей стороне . String.charAt () возвращает символ в позицииi
из оригинальной копии, поэтомуString.charAt()
будет быстрее, чемString.toCharArray()
. Хотя,String.toCharArray()
возвращает копию, а не char из исходного массива String, гдеString.charAt()
возвращает символ из исходного массива char. Код ниже возвращает значение по указанному индексу этой строки.приведенный ниже код возвращает вновь распределенный массив символов, длина которого равна длине этой строки
источник
Второй вызывает создание нового массива char, и все символы из String копируются в этот новый массив char, так что я бы предположил, что первый будет быстрее (и менее требователен к памяти).
источник