StringTokenizer
? Convertir le String
en un char[]
et répéter cela? Autre chose?
StringTokenizer
? Convertir le String
en un char[]
et répéter cela? Autre chose?
Réponses:
J'utilise une boucle for pour itérer la chaîne et charAt()
pour obtenir que chaque caractère l'examine. Puisque la chaîne est implémentée avec un tableau, la charAt()
méthode est une opération à temps constant.
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
Voilà ce que je ferais. Cela me semble le plus simple.
En ce qui concerne l'exactitude, je ne crois pas que cela existe ici. Tout est basé sur votre style personnel.
String.charAt(int)
fait simplement value[index]
. Je pense que vous confondez chatAt()
avec autre chose qui vous donne des points de code.
Deux options
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
ou
for(char c : s.toCharArray()) {
// process c
}
Le premier est probablement plus rapide, puis le 2e est probablement plus lisible.
Notez que la plupart des autres techniques décrites ici se décomposent si vous traitez avec des caractères en dehors du BMP (Unicode Basic Multilingual Plane ), c'est-à-dire des points de code qui sont en dehors de la plage u0000-uFFFF. Cela ne se produira que rarement, car les points de code en dehors de celui-ci sont principalement affectés à des langues mortes. Mais il y a quelques caractères utiles en dehors de cela, par exemple certains points de code utilisés pour la notation mathématique, et certains utilisés pour coder les noms propres en chinois.
Dans ce cas, votre code sera:
String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
int curChar = str.codePointAt(offset);
offset += Character.charCount(curChar);
// do something with curChar
}
La Character.charCount(int)
méthode nécessite Java 5+.
Je suis d'accord que StringTokenizer est exagéré ici. En fait, j'ai essayé les suggestions ci-dessus et j'ai pris le temps.
Mon test était assez simple: créer un StringBuilder avec environ un million de caractères, le convertir en une chaîne et parcourir chacun d'eux avec charAt () / après la conversion en un tableau de char / avec un CharacterIterator mille fois (bien sûr en veillant à faire quelque chose sur la chaîne pour que le compilateur ne puisse pas optimiser toute la boucle :-)).
Le résultat sur mon Powerbook 2,6 GHz (c'est un mac :-)) et JDK 1.5:
Les résultats étant sensiblement différents, le moyen le plus simple semble également être le plus rapide. Fait intéressant, charAt () d'un StringBuilder semble être légèrement plus lent que celui de String.
BTW Je suggère de ne pas utiliser CharacterIterator car je considère son abus du caractère '\ uFFFF' comme une "fin d'itération" un hack vraiment horrible. Dans les grands projets, il y a toujours deux gars qui utilisent le même type de hack à deux fins différentes et le code se bloque vraiment mystérieusement.
Voici l'un des tests:
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");
Dans Java 8, nous pouvons le résoudre comme:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
La méthode chars () renvoie un IntStream
comme mentionné dans la doc :
Renvoie un flux d'int entier étendant les valeurs char de cette séquence. Tout caractère mappé à un point de code de substitution est transmis sans interprétation. Si la séquence est mutée pendant la lecture du flux, le résultat n'est pas défini.
La méthode codePoints()
renvoie également un IntStream
document conforme à la doc:
Renvoie un flux de valeurs de points de code à partir de cette séquence. Toutes les paires de substitution rencontrées dans la séquence sont combinées comme si par Character.toCodePoint et le résultat est transmis au flux. Toutes les autres unités de code, y compris les caractères BMP ordinaires, les substituts non appariés et les unités de code non définies, sont étendues à zéro aux valeurs int qui sont ensuite transmises au flux.
En quoi le caractère et le point de code sont-ils différents? Comme mentionné dans cet article:
Unicode 3.1 a ajouté des caractères supplémentaires, ce qui porte le nombre total de caractères à plus de 216 caractères qui peuvent être distingués par un seul 16 bits
char
. Par conséquent, unechar
valeur n'a plus de mappage un à un avec l'unité sémantique fondamentale dans Unicode. JDK 5 a été mis à jour pour prendre en charge le plus grand ensemble de valeurs de caractères. Au lieu de changer la définition duchar
type, certains des nouveaux caractères supplémentaires sont représentés par une paire de substitution de deuxchar
valeurs. Pour réduire la confusion de dénomination, un point de code sera utilisé pour faire référence au nombre qui représente un caractère Unicode particulier, y compris les caractères supplémentaires.
Enfin pourquoi forEachOrdered
et non forEach
?
Le comportement de forEach
est explicitement non déterministe alors que le forEachOrdered
effectue une action pour chaque élément de ce flux, dans l' ordre de rencontre du flux si le flux a un ordre de rencontre défini. forEach
Ne garantit donc pas que la commande sera conservée. Consultez également cette question pour en savoir plus.
Pour la différence entre un caractère, un point de code, un glyphe et un graphème, vérifiez cette question .
Il existe des classes dédiées à cela:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
fournit. Un Java char
contient 16 bits et peut contenir des caractères Unicode jusqu'à U + FFFF mais Unicode spécifie des caractères jusqu'à U + 10FFFF. L'utilisation de 16 bits pour coder Unicode entraîne un codage de caractères de longueur variable. La plupart des réponses sur cette page supposent que l'encodage Java est un encodage de longueur constante, ce qui est faux.
Si vous avez Guava sur votre chemin de classe, ce qui suit est une alternative assez lisible. La goyave a même une implémentation de liste personnalisée assez sensible dans ce cas, donc cela ne devrait pas être inefficace.
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
MISE À JOUR: Comme l'a noté @Alex, avec Java 8, il y a aussi CharSequence#chars
à utiliser. Même le type est IntStream, il peut donc être mappé à des caractères comme:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
Si vous devez parcourir les points de code d'un String
(voir cette réponse ), un moyen plus court / plus lisible consiste à utiliser la CharSequence#codePoints
méthode ajoutée dans Java 8:
for(int c : string.codePoints().toArray()){
...
}
ou en utilisant le flux directement au lieu d'une boucle for:
string.codePoints().forEach(c -> ...);
Il y en a aussi CharSequence#chars
si vous voulez un flux de personnages (bien que ce soit un IntStream
, puisqu'il n'y en a pas CharStream
).
Je n'utiliserais pas StringTokenizer
car c'est l'une des classes du JDK qui est héritée.
Le javadoc dit:
StringTokenizer
est une classe héritée qui est conservée pour des raisons de compatibilité, bien que son utilisation soit déconseillée dans le nouveau code. Il est recommandé que toute personne recherchant cette fonctionnalité utilise à la place la méthode de fractionnementString
ou lejava.util.regex
package.
Si vous avez besoin de performances, vous devez tester sur votre environnement. Pas d'autre chemin.
Voici un exemple de code:
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);
Sur Java en ligne, je reçois:
1 10349420
2 526130
3 484200
0
Sur Android x86 API 17, j'obtiens:
1 9122107
2 13486911
3 12700778
0
Voir Les tutoriels Java: chaînes .
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);
}
}
Mettez la longueur dans int len
et utilisez la for
boucle.
StringTokenizer est totalement inadapté à la tâche de décomposer une chaîne en ses caractères individuels. Avec String#split()
vous pouvez le faire facilement en utilisant une expression régulière qui ne correspond à rien, par exemple:
String[] theChars = str.split("|");
Mais StringTokenizer n'utilise pas d'expressions régulières, et il n'y a pas de chaîne de délimiteur que vous pouvez spécifier qui correspondra au rien entre les caractères. Il y a un petit hack mignon que vous pouvez utiliser pour accomplir la même chose: utilisez la chaîne elle-même comme chaîne de délimiteur (en faisant de chaque caractère un délimiteur) et demandez-lui de renvoyer les délimiteurs:
StringTokenizer st = new StringTokenizer(str, str, true);
Cependant, je ne mentionne ces options que dans le but de les rejeter. Les deux techniques décomposent la chaîne d'origine en chaînes d'un caractère au lieu de primitives char, et impliquent toutes deux une surcharge importante sous forme de création d'objet et de manipulation de chaîne. Comparez cela à l'appel de charAt () dans une boucle for, qui n'entraîne pratiquement pas de surcharge.
Elaborer cette réponse et cette réponse .
Les réponses ci-dessus soulignent le problème de nombreuses solutions ici qui ne sont pas itérées par la valeur du point de code - elles auraient des problèmes avec les caractères de substitution . Les documents java décrivent également le problème ici (voir "Représentations de caractères Unicode"). Quoi qu'il en soit, voici un code qui utilise des caractères de substitution réelle de l'ensemble Unicode supplémentaire, et les convertit en arrière à une chaîne. Notez que .toChars () renvoie un tableau de caractères: si vous avez affaire à des substituts, vous aurez nécessairement deux caractères. Ce code devrait fonctionner pour tout caractère Unicode.
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
Cet exemple de code vous aidera!
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);
}
}
Donc, généralement, il y a deux façons d'itérer à travers une chaîne en java à laquelle plusieurs personnes ont déjà répondu dans ce fil, en ajoutant simplement ma version de celui-ci
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
Si les performances sont en jeu, je recommanderai d'utiliser la première en temps constant, si ce n'est pas le cas, la seconde facilite votre travail compte tenu de l'immuabilité avec les classes de chaînes en java.