Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

Что бы я сделал. Мне кажется самым легким для меня.

Что касается правильности, я не считаю, что существует здесь. Все это основано на вашем личном стиле.

Ответ 2

Две опции

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

или

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

Первый, вероятно, быстрее, а второй, вероятно, более читабельный.

Ответ 3

Обратите внимание, что большинство других описанных здесь методов разбиваются, если вы имеете дело с символами вне 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

Ответ 4

Я согласен, что StringTokenizer здесь переполнен. На самом деле я опробовал приведенные выше предложения и нашел время.

Мой тест был довольно прост: создайте StringBuilder с примерно миллионом символов, преобразуйте его в String и пройдите через каждый из них с помощью charAt()/после преобразования в массив char/с CharacterIterator тысячу раз ( конечно, сделав что-то в строке, чтобы компилятор не смог оптимизировать весь цикл:-)).

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

  • Тест 1: charAt + String → 3138msec
  • Тест 2: строка, преобразованная в массив → 9568 мсек
  • Тест 3: StringBuilder charAt → 3536msec
  • Тест 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<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");

Ответ 5

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

import java.text.*;

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

Ответ 6

Если у вас есть Guava в вашем пути к классам, следующее является довольно читаемой альтернативой. В этом случае 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

Ответ 7

Если вам нужно выполнить итерацию через кодовые точки String (см. этот ответ), более короткий/более читаемый способ - использовать CharSequence#codePoints добавлен в Java 8:

for(int c : string.codePoints().toArray()){
    ...
}

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

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

Существует также CharSequence#chars, если вы хотите поток символов (хотя это IntStream, так как нет CharStream).

Ответ 8

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

В javadoc говорится:

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

Ответ 9

В 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:

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

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

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

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

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

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

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

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

Ответ 10

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

Ответ 11

StringTokenizer полностью не подходит для задачи разбиения строки на отдельные символы. С помощью String#split() вы можете сделать это легко, используя регулярное выражение, которое ничего не соответствует, например:

String[] theChars = str.split("|");

Но StringTokenizer не использует регулярные выражения, и нет строки разделителя, которую вы можете указать, которая не будет содержать ничего между символами. Существует один милый маленький взлом, который вы можете использовать для достижения того же самого: используйте строку как строку разделителя (делая каждый символ в ней разделителем) и возвращайте разделители:

StringTokenizer st = new StringTokenizer(str, str, true);

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

Ответ 12

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

В приведенных выше ответах указывается на проблему многих решений, которые не повторяются по кодовой точке - у них возникнут проблемы с любым суррогатными символами. В java-документах также описывается проблема здесь (см. "Представления символов Unicode" ). Во всяком случае, здесь некоторый код, который использует некоторые фактические суррогатные символы из дополнительного набора Unicode и преобразует их обратно в String. Обратите внимание, что .toChars() возвращает массив символов: если вы имеете дело с суррогатами, у вас обязательно будет два символа. Этот код должен работать для любого символа Юникода.

    String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
    supplementary.codePoints().forEach(cp -> 
            System.out.print(new String(Character.toChars(cp))));

Ответ 13

Этот пример кода поможет вам!

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);
    }
}