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
}
Перший, ймовірно, швидший, а потім 2-й, мабуть, читабельніший.
Зверніть увагу, що більшість інших описаних тут методів розбиваються, якщо ви маєте справу з символами поза 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+.
Я згоден, що StringTokenizer тут непосильний. Насправді я випробував запропоновані вище пропозиції і знайшов час.
Мій тест був досить простим: створити StringBuilder з близько мільйона символів, перетворити його в String і пройти кожен з них за допомогою charAt () / після перетворення в масив char / тисячу разів (звичайно переконуючись, що зробіть щось у рядку, щоб компілятор не зміг оптимізувати весь цикл :-)).
Результат у моїй книжці 2,6 ГГц (це мак :-)) та JDK 1,5:
Оскільки результати значно відрізняються, найпростіший спосіб також видається найшвидшим. Цікаво, що charAt () StringBuilder здається трохи повільнішим, ніж у StringBuilder.
BTW Я пропоную не використовувати 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
як зазначено в doc :
Повертає потік int, що розширює значення char із цієї послідовності. Будь-яка таблиця, яка відображається в сурогатній кодовій точці, передається через неінтерпретоване. Якщо послідовність мутується під час читання потоку, результат не визначений.
Метод codePoints()
також повертає IntStream
як на документ:
Повертає потік значень точок коду з цієї послідовності. Будь-які сурогатні пари, що зустрічаються в послідовності, поєднуються як би символом.toCodePoint і результат передається в потік. Будь-які інші одиниці коду, включаючи звичайні символи BMP, парні сурогати та неозначені кодові одиниці, розширюються на нуль до значень int, які потім передаються в потік.
Чим відрізняються char та code? Як згадується в цій статті:
Unicode 3.1 додав додаткові символи, що збільшить загальну кількість символів більше ніж 216 символів, які можна розрізнити одним 16-бітовим
char
. Отже,char
значення більше не має відображення один на один до основної смислової одиниці в Unicode. JDK 5 було оновлено для підтримки більшого набору знакових значень. Замість зміни визначенняchar
типу деякі нові додаткові символи представлені сурогатною парою з двохchar
значень. Щоб зменшити плутанину імен, буде використана кодова точка для позначення числа, що представляє певний символ Unicode, включаючи додаткові.
Нарешті чому forEachOrdered
і ні forEach
?
Поведінка forEach
явно недетерміновано там, де 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 навіть є досить розумна реалізація спеціального списку для цього випадку, тому це не повинно бути неефективним.
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()){
...
}
або використовуючи потік безпосередньо замість циклу:
string.codePoints().forEach(c -> ...);
Існує також, CharSequence#chars
якщо ви хочете потік символів (хоча це і є IntStream
, оскільки немає CharStream
).
Я б не використовував, StringTokenizer
оскільки це один із класів у JDK, який є спадщиною.
Явадок каже:
StringTokenizer
це старий клас, який зберігається з міркувань сумісності, хоча його використання не перешкоджає новому коду. Рекомендується, щоб кожен, хто шукає цю функціональність, використовував метод розділенняString
або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);
Однак я згадую лише ці варіанти з метою їх звільнення. Обидві методи розбивають початковий рядок на односимвольні рядки замість char примітивів, і обидва передбачають великі накладні витрати у вигляді створення об'єкта та маніпуляції з рядками. Порівняйте це з викликом charAt () у циклі for, який практично не має накладних витрат.
Розвиваючи цю відповідь і цю відповідь .
Наведені вище відповіді вказують на проблему багатьох тут рішень, які не повторюються за значенням кодової точки - у них виникнуть проблеми з будь-якими сурогатними знаками . Документи Java також окреслюють проблему тут (див. "Представлення символів Unicode"). У будь-якому випадку, ось якийсь код, який використовує деякі фактичні сурогатні символи з додаткового набору Unicode і перетворює їх назад у String. Зауважте, що .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.