StringTokenizer
? Преобразовать String
к char[]
и итерации над этим? Что-то другое?
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
}
Я бы так и сделал. Это кажется самым легким для меня.
Что касается правильности, я не верю, что это существует здесь. Все это основано на вашем личном стиле.
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 ), то есть кодовые точки, которые находятся за пределами диапазона 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 () / после преобразования в массив символов / с CharacterIterator тысячу раз (конечно, убедившись, что сделайте что-нибудь со строкой, чтобы компилятор не мог оптимизировать весь цикл :-)).
Результат на моем Powerbook 2.6 ГГц (это mac :-)) и JDK 1.5:
Поскольку результаты значительно отличаются, самый простой способ также кажется самым быстрым. Интересно, что 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");
В 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
указанное в документе :
Возвращает поток значений int, расширяющих ноль, из этой последовательности. Любой символ, который отображается на суррогатную кодовую точку, пропускается через интерпретацию. Если последовательность видоизменяется во время чтения потока, результат не определен.
Метод codePoints()
также возвращает IntStream
согласно документу:
Возвращает поток значений кодовой точки из этой последовательности. Любые суррогатные пары, встречающиеся в последовательности, объединяются как бы с помощью Character.toCodePoint, и результат передается в поток. Любые другие единицы кода, включая обычные символы BMP, непарные суррогаты и неопределенные единицы кода, расширяются от нуля до значений int, которые затем передаются в поток.
Чем отличаются символ и код? Как уже упоминалось в этой статье:
В Unicode 3.1 добавлены дополнительные символы, в результате чего общее количество символов превышает 216 символов, которые можно различить одним 16-разрядным
char
. Поэтомуchar
значение больше не имеет однозначного сопоставления с основной семантической единицей в Юникоде. JDK 5 был обновлен для поддержки большего набора символьных значений. Вместо изменения определенияchar
типа, некоторые из новых дополнительных символов представлены суррогатной парой двухchar
значений. Чтобы уменьшить путаницу имен, будет использоваться кодовая точка для обозначения номера, представляющего конкретный символ Unicode, включая дополнительные.
Наконец почему forEachOrdered
и нет forEach
?
Поведение forEach
является явно недетерминированным, когда as forEachOrdered
выполняет действие для каждого элемента этого потока, в порядке обнаружения потока, если поток имеет определенный порядок встречи. Так forEach
что не гарантирует, что заказ будет сохранен. Также проверьте этот вопрос для получения дополнительной информации.
Для различия между символом, кодовой точкой, глифом и графемой, проверьте этот вопрос .
Для этого есть несколько специальных классов:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
обеспечивает Java . Java char
содержит 16 бит и может содержать символы Unicode до U + FFFF, но Unicode определяет символы до U + 10FFFF. Использование 16 битов для кодирования Unicode приводит к кодированию символов переменной длины. Большинство ответов на этой странице предполагают, что кодировка Java является кодировкой постоянной длины, что неверно.
Если у вас есть Guava на вашем пути к классам, следующее является довольно удобочитаемой альтернативой. В Guava даже есть довольно разумная реализация 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()){
...
}
или используя поток вместо цикла for:
string.codePoints().forEach(c -> ...);
Существует также, CharSequence#chars
если вы хотите, чтобы поток символов (хотя это IntStream
, так как нет CharStream
).
Я бы не стал использовать, так StringTokenizer
как это один из классов в JDK, который унаследован.
Javadoc говорит:
StringTokenizer
является устаревшим классом, который сохраняется по соображениям совместимости, хотя его использование не рекомендуется в новом коде. Всем, кто ищет эту функцию, рекомендуется вместо этого использовать метод splitString
или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);
На Java онлайн я получаю:
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);
Однако я упоминаю только эти варианты с целью их отклонения. Оба метода разбивают исходную строку на односимвольные строки вместо символьных примитивов, и оба требуют больших накладных расходов в виде создания объекта и манипуляции со строками. Сравните это с вызовом charAt () в цикле for, который практически не требует дополнительных затрат.
Разрабатывая этот ответ и этот ответ .
Приведенные выше ответы указывают на проблему многих решений, которые здесь не повторяются по значению кодовой точки - у них возникнут проблемы с любыми суррогатными символами . Документы Java также описывают проблему здесь (см. «Представления символов Unicode»). Во всяком случае, вот некоторый код, который использует некоторые фактические суррогатные символы из дополнительного набора Unicode и преобразует их обратно в строку. Обратите внимание, что .toChars () возвращает массив символов: если вы имеете дело с суррогатами, у вас обязательно будет два символа. Этот код должен работать для любого символа Unicode.
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.