Каков самый простой / лучший / самый правильный способ перебора символов строки в Java?

StringTokenizer ? Преобразовать String в char[] и перебрать ее? Что-то другое?

Я использую цикл for для итерации строки и использования charAt() чтобы каждый персонаж мог ее изучить. Поскольку String реализуется с помощью массива, метод 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), то есть с кодами , находящимися за пределами диапазона 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 с примерно миллионом символов, преобразуйте его в String и перейдите по каждому из них с помощью charAt () / после преобразования в массив символов / с CharacterIterator тысячу раз (конечно, убедившись, что сделайте что-нибудь в строке, чтобы компилятор не смог оптимизировать весь цикл :-)).

Результат на моем 2,6 ГГц Powerbook (это mac :-)) и JDK 1.5:

  • Тест 1: charAt + String -> 3138msec
  • Тест 2: строка, преобразованная в массив -> 9568 мсек
  • Тест 3: StringBuilder charAt -> 3536мс
  • Тест 4: CharacterIterator и String -> 12151msec

Поскольку результаты существенно различаются, наиболее простой способ, по-видимому, является самым быстрым. Интересно, что charAt () для StringBuilder выглядит немного медленнее, чем у String.

Кстати, я предлагаю не использовать CharacterIterator, поскольку я считаю его злоупотребление символом ‘\ uFFFF’ как «конец итерации» действительно ужасным взломом. В крупных проектах всегда есть два парня, которые используют один и тот же хак для двух разных целей, и код действительно сбивает с толку.

Вот один из тестов:

  int count = 1000; ... System.out.println("Test 1: charAt + String"); long t = System.currentTimeMillis(); int sum=0; for (int i=0; i 

Для этого есть несколько выделенных classов:

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

Если у вас есть Guava на пути к classу, следующее является довольно читаемой альтернативой. В этом случае Guava даже имеет довольно разумную реализацию Custom List, поэтому это не должно быть неэффективным.

 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()){ ... } 

или используя stream непосредственно вместо цикла for:

 string.codePoints().forEach(c -> ...); 

Есть также CharSequence#chars если вы хотите stream символов (хотя это IntStream , так как нет CharStream ).

В 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 как указано в документе :

Возвращает stream int zero, расширяющий значения char из этой последовательности. Любой символ, который сопоставляется суррогатной кодовой точке, передается через неинтерпретируемый. Если последовательность мутируется во время чтения streamа, результат не определен.

Метод codePoints() также возвращает IntStream в соответствии с документом:

Возвращает stream значений кодовой точки из этой последовательности. Любые суррогатные пары, встречающиеся в последовательности, объединены, как если бы с помощью Character.toCodePoint, и результат передается streamу. Любые другие единицы кода, включая обычные символы BMP, непарные суррогаты и неопределенные кодовые единицы, имеют нулевое расширение до значений int, которые затем передаются в stream.

Как отличается символ char и code? Как упоминалось в этой статье:

В Unicode 3.1 добавлены дополнительные символы, в результате чего общее количество символов превышает 216 символов, которые можно выделить одним 16-битным char . Следовательно, значение char больше не имеет взаимно однозначного соответствия фундаментальной семантической единице в Unicode. JDK 5 был обновлен для поддержки большего набора значений символов. Вместо изменения определения типа char некоторые из новых дополнительных символов представлены суррогатной парой из двух значений char . Чтобы уменьшить путаницу в именах, кодовая точка будет использоваться для обозначения числа, которое представляет конкретный символ Юникода, включая дополнительные.

Наконец, почему forEachOrdered а не forEach ?

Поведение forEach явно недетерминировано, когда forEachOrdered выполняет действие для каждого элемента этого streamа, в порядке обнаружения streamа, если stream имеет определенный порядок forEachOrdered . Поэтому forEach не гарантирует, что заказ будет сохранен. Также проверьте этот вопрос для получения дополнительной информации.

Для разницы между символом, точкой кода, глифом и графемой проверьте этот вопрос .

Я бы не использовал StringTokenizer поскольку это один из classов в JDK, который является наследием.

Джавадок говорит:

StringTokenizer – это унаследованный class, который сохраняется по соображениям совместимости, хотя его использование не рекомендуется в новом коде. Рекомендуется, чтобы любой, кто ищет эту функциональность, использовал метод split для String или пакет java.util.regex .

См . Учебники 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); 

Однако я упоминаю только эти варианты с целью их увольнения. Оба метода ломают исходную строку в односимвольные строки вместо примитивов char, и оба связаны с большими накладными расходами в виде создания объектов и строковых манипуляций. Сравните это с вызовом charAt () в цикле for, который фактически не наносит накладных расходов.

Разрабатываем этот ответ и этот ответ .

В приведенных выше ответах указывается на проблему многих решений, которые не повторяются по кодовой точке – у них возникнут проблемы с любыми суррогатными символами . В java-документах также описывается проблема (см. «Представления символов Unicode»). Во всяком случае, вот какой-то код, который использует некоторые реальные суррогатные символы из дополнительного набора Unicode и преобразует их обратно в String. Обратите внимание, что .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 map = new HashMap(); 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); } } 
  • Чистые файлы исходного кода невидимых символов
  • преобразовать формат столбца data.frame из символа в фактор
  • Индексы всех вхождений символа в строку
  • Почему нам нужно поставить пробел до% c?
  • Давайте будем гением компьютера.